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); 190b7579f77SDag-Erling Smørgrav printf("-x path pathname to xml in url, default %s\n", XMLNAME); 191b7579f77SDag-Erling Smørgrav printf("-s path pathname to p7s in url, default %s\n", P7SNAME); 1928ed2b524SDag-Erling Smørgrav printf("-n name signer's subject emailAddress, default %s\n", P7SIGNER); 193b7579f77SDag-Erling Smørgrav printf("-4 work using IPv4 only\n"); 194b7579f77SDag-Erling Smørgrav printf("-6 work using IPv6 only\n"); 195*4c75e3aaSDag-Erling Smørgrav printf("-f resolv.conf use given resolv.conf\n"); 196*4c75e3aaSDag-Erling Smørgrav printf("-r root.hints use given root.hints\n" 197b7579f77SDag-Erling Smørgrav " builtin root hints are used by default\n"); 198*4c75e3aaSDag-Erling Smørgrav printf("-R fallback from -f to root query on error\n"); 199b7579f77SDag-Erling Smørgrav printf("-v more verbose\n"); 200b7579f77SDag-Erling Smørgrav printf("-C conf debug, read config\n"); 201b7579f77SDag-Erling Smørgrav printf("-P port use port for https connect, default 443\n"); 202b7579f77SDag-Erling Smørgrav printf("-F debug, force update with cert\n"); 203b7579f77SDag-Erling Smørgrav printf("-h show this usage help\n"); 204b7579f77SDag-Erling Smørgrav printf("Version %s\n", PACKAGE_VERSION); 205b7579f77SDag-Erling Smørgrav printf("BSD licensed, see LICENSE in source package for details.\n"); 206b7579f77SDag-Erling Smørgrav printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 207b7579f77SDag-Erling Smørgrav exit(1); 208b7579f77SDag-Erling Smørgrav } 209b7579f77SDag-Erling Smørgrav 210b7579f77SDag-Erling Smørgrav /** return the built in root update certificate */ 211b7579f77SDag-Erling Smørgrav static const char* 212b7579f77SDag-Erling Smørgrav get_builtin_cert(void) 213b7579f77SDag-Erling Smørgrav { 214b7579f77SDag-Erling Smørgrav return 215b7579f77SDag-Erling Smørgrav /* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */ 216b7579f77SDag-Erling Smørgrav "-----BEGIN CERTIFICATE-----\n" 217b7579f77SDag-Erling Smørgrav "MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" 218b7579f77SDag-Erling Smørgrav "TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" 219b7579f77SDag-Erling Smørgrav "BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n" 220b7579f77SDag-Erling Smørgrav "DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n" 221b7579f77SDag-Erling Smørgrav "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n" 222b7579f77SDag-Erling Smørgrav "MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n" 223b7579f77SDag-Erling Smørgrav "cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n" 224b7579f77SDag-Erling Smørgrav "G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n" 225b7579f77SDag-Erling Smørgrav "ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n" 226b7579f77SDag-Erling Smørgrav "paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n" 227b7579f77SDag-Erling Smørgrav "MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n" 228b7579f77SDag-Erling Smørgrav "iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" 229b7579f77SDag-Erling Smørgrav "Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n" 230b7579f77SDag-Erling Smørgrav "DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n" 231b7579f77SDag-Erling Smørgrav "6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n" 232b7579f77SDag-Erling Smørgrav "2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n" 233b7579f77SDag-Erling Smørgrav "15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n" 234b7579f77SDag-Erling Smørgrav "0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n" 235b7579f77SDag-Erling Smørgrav "j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" 236b7579f77SDag-Erling Smørgrav "-----END CERTIFICATE-----\n" 237b7579f77SDag-Erling Smørgrav ; 238b7579f77SDag-Erling Smørgrav } 239b7579f77SDag-Erling Smørgrav 240b7579f77SDag-Erling Smørgrav /** return the built in root DS trust anchor */ 241b7579f77SDag-Erling Smørgrav static const char* 242b7579f77SDag-Erling Smørgrav get_builtin_ds(void) 243b7579f77SDag-Erling Smørgrav { 244b7579f77SDag-Erling Smørgrav return 245c7f4d7adSDag-Erling Smørgrav /* The anchors must start on a new line with ". IN DS and end with \n"[;] 246c7f4d7adSDag-Erling Smørgrav * because the makedist script greps on the source here */ 2473005e0a3SDag-Erling Smørgrav /* anchor 19036 is from 2010 */ 2483005e0a3SDag-Erling Smørgrav /* anchor 20326 is from 2017 */ 2493005e0a3SDag-Erling Smørgrav ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n" 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, 280ebc5657fSDag-Erling Smørgrav const char* debugconf, 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 } 304b7579f77SDag-Erling Smørgrav if(ip4only) { 305b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "do-ip6:", "no"); 306b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, "ip4only", ub_strerror(r)); 307b7579f77SDag-Erling Smørgrav } 308b7579f77SDag-Erling Smørgrav if(ip6only) { 309b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "do-ip4:", "no"); 310b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, "ip6only", ub_strerror(r)); 311b7579f77SDag-Erling Smørgrav } 312b7579f77SDag-Erling Smørgrav return ctx; 313b7579f77SDag-Erling Smørgrav } 314b7579f77SDag-Erling Smørgrav 315b7579f77SDag-Erling Smørgrav /** printout certificate in detail */ 316b7579f77SDag-Erling Smørgrav static void 317ebc5657fSDag-Erling Smørgrav verb_cert(const char* msg, X509* x) 318b7579f77SDag-Erling Smørgrav { 319b7579f77SDag-Erling Smørgrav if(verb == 0 || verb == 1) return; 320b7579f77SDag-Erling Smørgrav if(verb == 2) { 321b7579f77SDag-Erling Smørgrav if(msg) printf("%s\n", msg); 322b7579f77SDag-Erling Smørgrav X509_print_ex_fp(stdout, x, 0, (unsigned long)-1 323b7579f77SDag-Erling Smørgrav ^(X509_FLAG_NO_SUBJECT 324b7579f77SDag-Erling Smørgrav |X509_FLAG_NO_ISSUER|X509_FLAG_NO_VALIDITY)); 325b7579f77SDag-Erling Smørgrav return; 326b7579f77SDag-Erling Smørgrav } 327b7579f77SDag-Erling Smørgrav if(msg) printf("%s\n", msg); 328b7579f77SDag-Erling Smørgrav X509_print_fp(stdout, x); 329b7579f77SDag-Erling Smørgrav } 330b7579f77SDag-Erling Smørgrav 331b7579f77SDag-Erling Smørgrav /** printout certificates in detail */ 332b7579f77SDag-Erling Smørgrav static void 333ebc5657fSDag-Erling Smørgrav verb_certs(const char* msg, STACK_OF(X509)* sk) 334b7579f77SDag-Erling Smørgrav { 335b7579f77SDag-Erling Smørgrav int i, num = sk_X509_num(sk); 336b7579f77SDag-Erling Smørgrav if(verb == 0 || verb == 1) return; 337b7579f77SDag-Erling Smørgrav for(i=0; i<num; i++) { 338b7579f77SDag-Erling Smørgrav printf("%s (%d/%d)\n", msg, i, num); 339b7579f77SDag-Erling Smørgrav verb_cert(NULL, sk_X509_value(sk, i)); 340b7579f77SDag-Erling Smørgrav } 341b7579f77SDag-Erling Smørgrav } 342b7579f77SDag-Erling Smørgrav 343b7579f77SDag-Erling Smørgrav /** read certificates from a PEM bio */ 344b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 345b7579f77SDag-Erling Smørgrav read_cert_bio(BIO* bio) 346b7579f77SDag-Erling Smørgrav { 347b7579f77SDag-Erling Smørgrav STACK_OF(X509) *sk = sk_X509_new_null(); 348b7579f77SDag-Erling Smørgrav if(!sk) { 349b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 350b7579f77SDag-Erling Smørgrav exit(0); 351b7579f77SDag-Erling Smørgrav } 352b7579f77SDag-Erling Smørgrav while(!BIO_eof(bio)) { 353b7579f77SDag-Erling Smørgrav X509* x = PEM_read_bio_X509(bio, NULL, 0, NULL); 354b7579f77SDag-Erling Smørgrav if(x == NULL) { 355b7579f77SDag-Erling Smørgrav if(verb) { 356b7579f77SDag-Erling Smørgrav printf("failed to read X509\n"); 357b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 358b7579f77SDag-Erling Smørgrav } 359b7579f77SDag-Erling Smørgrav continue; 360b7579f77SDag-Erling Smørgrav } 361b7579f77SDag-Erling Smørgrav if(!sk_X509_push(sk, x)) { 362b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 363b7579f77SDag-Erling Smørgrav exit(0); 364b7579f77SDag-Erling Smørgrav } 365b7579f77SDag-Erling Smørgrav } 366b7579f77SDag-Erling Smørgrav return sk; 367b7579f77SDag-Erling Smørgrav } 368b7579f77SDag-Erling Smørgrav 369b7579f77SDag-Erling Smørgrav /* read the certificate file */ 370b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 371ebc5657fSDag-Erling Smørgrav read_cert_file(const char* file) 372b7579f77SDag-Erling Smørgrav { 373b7579f77SDag-Erling Smørgrav STACK_OF(X509)* sk; 374b7579f77SDag-Erling Smørgrav FILE* in; 375b7579f77SDag-Erling Smørgrav int content = 0; 376b7579f77SDag-Erling Smørgrav char buf[128]; 377b7579f77SDag-Erling Smørgrav if(file == NULL || strcmp(file, "") == 0) { 378b7579f77SDag-Erling Smørgrav return NULL; 379b7579f77SDag-Erling Smørgrav } 380b7579f77SDag-Erling Smørgrav sk = sk_X509_new_null(); 381b7579f77SDag-Erling Smørgrav if(!sk) { 382b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 383b7579f77SDag-Erling Smørgrav exit(0); 384b7579f77SDag-Erling Smørgrav } 385b7579f77SDag-Erling Smørgrav in = fopen(file, "r"); 386b7579f77SDag-Erling Smørgrav if(!in) { 387b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 388b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 389b7579f77SDag-Erling Smørgrav sk_X509_pop_free(sk, X509_free); 390b7579f77SDag-Erling Smørgrav #endif 391b7579f77SDag-Erling Smørgrav return NULL; 392b7579f77SDag-Erling Smørgrav } 393b7579f77SDag-Erling Smørgrav while(!feof(in)) { 394b7579f77SDag-Erling Smørgrav X509* x = PEM_read_X509(in, NULL, 0, NULL); 395b7579f77SDag-Erling Smørgrav if(x == NULL) { 396b7579f77SDag-Erling Smørgrav if(verb) { 397b7579f77SDag-Erling Smørgrav printf("failed to read X509 file\n"); 398b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 399b7579f77SDag-Erling Smørgrav } 400b7579f77SDag-Erling Smørgrav continue; 401b7579f77SDag-Erling Smørgrav } 402b7579f77SDag-Erling Smørgrav if(!sk_X509_push(sk, x)) { 403b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 404b7579f77SDag-Erling Smørgrav fclose(in); 405b7579f77SDag-Erling Smørgrav exit(0); 406b7579f77SDag-Erling Smørgrav } 407b7579f77SDag-Erling Smørgrav content = 1; 408b7579f77SDag-Erling Smørgrav /* read away newline after --END CERT-- */ 409b7579f77SDag-Erling Smørgrav if(!fgets(buf, (int)sizeof(buf), in)) 410b7579f77SDag-Erling Smørgrav break; 411b7579f77SDag-Erling Smørgrav } 412b7579f77SDag-Erling Smørgrav fclose(in); 413b7579f77SDag-Erling Smørgrav if(!content) { 414b7579f77SDag-Erling Smørgrav if(verb) printf("%s is empty\n", file); 415b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 416b7579f77SDag-Erling Smørgrav sk_X509_pop_free(sk, X509_free); 417b7579f77SDag-Erling Smørgrav #endif 418b7579f77SDag-Erling Smørgrav return NULL; 419b7579f77SDag-Erling Smørgrav } 420b7579f77SDag-Erling Smørgrav return sk; 421b7579f77SDag-Erling Smørgrav } 422b7579f77SDag-Erling Smørgrav 423b7579f77SDag-Erling Smørgrav /** read certificates from the builtin certificate */ 424b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 425b7579f77SDag-Erling Smørgrav read_builtin_cert(void) 426b7579f77SDag-Erling Smørgrav { 427b7579f77SDag-Erling Smørgrav const char* builtin_cert = get_builtin_cert(); 428b7579f77SDag-Erling Smørgrav STACK_OF(X509)* sk; 429b5663de9SDag-Erling Smørgrav BIO *bio = BIO_new_mem_buf(builtin_cert, 430b7579f77SDag-Erling Smørgrav (int)strlen(builtin_cert)); 431b7579f77SDag-Erling Smørgrav if(!bio) { 432b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 433b7579f77SDag-Erling Smørgrav exit(0); 434b7579f77SDag-Erling Smørgrav } 435b7579f77SDag-Erling Smørgrav sk = read_cert_bio(bio); 436b7579f77SDag-Erling Smørgrav if(!sk) { 437b7579f77SDag-Erling Smørgrav if(verb) printf("internal error, out of memory\n"); 438b7579f77SDag-Erling Smørgrav exit(0); 439b7579f77SDag-Erling Smørgrav } 440b7579f77SDag-Erling Smørgrav BIO_free(bio); 441b7579f77SDag-Erling Smørgrav return sk; 442b7579f77SDag-Erling Smørgrav } 443b7579f77SDag-Erling Smørgrav 444b7579f77SDag-Erling Smørgrav /** read update cert file or use builtin */ 445b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 446ebc5657fSDag-Erling Smørgrav read_cert_or_builtin(const char* file) 447b7579f77SDag-Erling Smørgrav { 448b7579f77SDag-Erling Smørgrav STACK_OF(X509) *sk = read_cert_file(file); 449b7579f77SDag-Erling Smørgrav if(!sk) { 450b7579f77SDag-Erling Smørgrav if(verb) printf("using builtin certificate\n"); 451b7579f77SDag-Erling Smørgrav sk = read_builtin_cert(); 452b7579f77SDag-Erling Smørgrav } 453b7579f77SDag-Erling Smørgrav if(verb) printf("have %d trusted certificates\n", sk_X509_num(sk)); 454b7579f77SDag-Erling Smørgrav verb_certs("trusted certificates", sk); 455b7579f77SDag-Erling Smørgrav return sk; 456b7579f77SDag-Erling Smørgrav } 457b7579f77SDag-Erling Smørgrav 458b7579f77SDag-Erling Smørgrav static void 459b7579f77SDag-Erling Smørgrav do_list_builtin(void) 460b7579f77SDag-Erling Smørgrav { 461b7579f77SDag-Erling Smørgrav const char* builtin_cert = get_builtin_cert(); 462b7579f77SDag-Erling Smørgrav const char* builtin_ds = get_builtin_ds(); 463b7579f77SDag-Erling Smørgrav printf("%s\n", builtin_ds); 464b7579f77SDag-Erling Smørgrav printf("%s\n", builtin_cert); 465b7579f77SDag-Erling Smørgrav exit(0); 466b7579f77SDag-Erling Smørgrav } 467b7579f77SDag-Erling Smørgrav 468b7579f77SDag-Erling Smørgrav /** printout IP address with message */ 469b7579f77SDag-Erling Smørgrav static void 470ebc5657fSDag-Erling Smørgrav verb_addr(const char* msg, struct ip_list* ip) 471b7579f77SDag-Erling Smørgrav { 472b7579f77SDag-Erling Smørgrav if(verb) { 473b7579f77SDag-Erling Smørgrav char out[100]; 474b7579f77SDag-Erling Smørgrav void* a = &((struct sockaddr_in*)&ip->addr)->sin_addr; 475b7579f77SDag-Erling Smørgrav if(ip->len != (socklen_t)sizeof(struct sockaddr_in)) 476b7579f77SDag-Erling Smørgrav a = &((struct sockaddr_in6*)&ip->addr)->sin6_addr; 477b7579f77SDag-Erling Smørgrav 478b7579f77SDag-Erling Smørgrav if(inet_ntop((int)((struct sockaddr_in*)&ip->addr)->sin_family, 479b7579f77SDag-Erling Smørgrav a, out, (socklen_t)sizeof(out))==0) 480b7579f77SDag-Erling Smørgrav printf("%s (inet_ntop error)\n", msg); 481b7579f77SDag-Erling Smørgrav else printf("%s %s\n", msg, out); 482b7579f77SDag-Erling Smørgrav } 483b7579f77SDag-Erling Smørgrav } 484b7579f77SDag-Erling Smørgrav 485b7579f77SDag-Erling Smørgrav /** free ip_list */ 486b7579f77SDag-Erling Smørgrav static void 487b7579f77SDag-Erling Smørgrav ip_list_free(struct ip_list* p) 488b7579f77SDag-Erling Smørgrav { 489b7579f77SDag-Erling Smørgrav struct ip_list* np; 490b7579f77SDag-Erling Smørgrav while(p) { 491b7579f77SDag-Erling Smørgrav np = p->next; 492b7579f77SDag-Erling Smørgrav free(p); 493b7579f77SDag-Erling Smørgrav p = np; 494b7579f77SDag-Erling Smørgrav } 495b7579f77SDag-Erling Smørgrav } 496b7579f77SDag-Erling Smørgrav 497b7579f77SDag-Erling Smørgrav /** create ip_list entry for a RR record */ 498b7579f77SDag-Erling Smørgrav static struct ip_list* 499b7579f77SDag-Erling Smørgrav RR_to_ip(int tp, char* data, int len, int port) 500b7579f77SDag-Erling Smørgrav { 501b7579f77SDag-Erling Smørgrav struct ip_list* ip = (struct ip_list*)calloc(1, sizeof(*ip)); 502b7579f77SDag-Erling Smørgrav uint16_t p = (uint16_t)port; 503b7579f77SDag-Erling Smørgrav if(tp == LDNS_RR_TYPE_A) { 504b7579f77SDag-Erling Smørgrav struct sockaddr_in* sa = (struct sockaddr_in*)&ip->addr; 505b7579f77SDag-Erling Smørgrav ip->len = (socklen_t)sizeof(*sa); 506b7579f77SDag-Erling Smørgrav sa->sin_family = AF_INET; 507b7579f77SDag-Erling Smørgrav sa->sin_port = (in_port_t)htons(p); 508b7579f77SDag-Erling Smørgrav if(len != (int)sizeof(sa->sin_addr)) { 509b7579f77SDag-Erling Smørgrav if(verb) printf("skipped badly formatted A\n"); 510b7579f77SDag-Erling Smørgrav free(ip); 511b7579f77SDag-Erling Smørgrav return NULL; 512b7579f77SDag-Erling Smørgrav } 513b7579f77SDag-Erling Smørgrav memmove(&sa->sin_addr, data, sizeof(sa->sin_addr)); 514b7579f77SDag-Erling Smørgrav 515b7579f77SDag-Erling Smørgrav } else if(tp == LDNS_RR_TYPE_AAAA) { 516b7579f77SDag-Erling Smørgrav struct sockaddr_in6* sa = (struct sockaddr_in6*)&ip->addr; 517b7579f77SDag-Erling Smørgrav ip->len = (socklen_t)sizeof(*sa); 518b7579f77SDag-Erling Smørgrav sa->sin6_family = AF_INET6; 519b7579f77SDag-Erling Smørgrav sa->sin6_port = (in_port_t)htons(p); 520b7579f77SDag-Erling Smørgrav if(len != (int)sizeof(sa->sin6_addr)) { 521b7579f77SDag-Erling Smørgrav if(verb) printf("skipped badly formatted AAAA\n"); 522b7579f77SDag-Erling Smørgrav free(ip); 523b7579f77SDag-Erling Smørgrav return NULL; 524b7579f77SDag-Erling Smørgrav } 525b7579f77SDag-Erling Smørgrav memmove(&sa->sin6_addr, data, sizeof(sa->sin6_addr)); 526b7579f77SDag-Erling Smørgrav } else { 527b7579f77SDag-Erling Smørgrav if(verb) printf("internal error: bad type in RRtoip\n"); 528b7579f77SDag-Erling Smørgrav free(ip); 529b7579f77SDag-Erling Smørgrav return NULL; 530b7579f77SDag-Erling Smørgrav } 531b7579f77SDag-Erling Smørgrav verb_addr("resolved server address", ip); 532b7579f77SDag-Erling Smørgrav return ip; 533b7579f77SDag-Erling Smørgrav } 534b7579f77SDag-Erling Smørgrav 535b7579f77SDag-Erling Smørgrav /** Resolve name, type, class and add addresses to iplist */ 536b7579f77SDag-Erling Smørgrav static void 537ebc5657fSDag-Erling Smørgrav resolve_host_ip(struct ub_ctx* ctx, const char* host, int port, int tp, int cl, 538b7579f77SDag-Erling Smørgrav struct ip_list** head) 539b7579f77SDag-Erling Smørgrav { 540b7579f77SDag-Erling Smørgrav struct ub_result* res = NULL; 541b7579f77SDag-Erling Smørgrav int r; 542b7579f77SDag-Erling Smørgrav int i; 543b7579f77SDag-Erling Smørgrav 544b7579f77SDag-Erling Smørgrav r = ub_resolve(ctx, host, tp, cl, &res); 545b7579f77SDag-Erling Smørgrav if(r) { 546b7579f77SDag-Erling Smørgrav if(verb) printf("error: resolve %s %s: %s\n", host, 547b7579f77SDag-Erling Smørgrav (tp==LDNS_RR_TYPE_A)?"A":"AAAA", ub_strerror(r)); 548b7579f77SDag-Erling Smørgrav return; 549b7579f77SDag-Erling Smørgrav } 550b7579f77SDag-Erling Smørgrav if(!res) { 551b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 552b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 553b7579f77SDag-Erling Smørgrav exit(0); 554b7579f77SDag-Erling Smørgrav } 5558ed2b524SDag-Erling Smørgrav if(!res->havedata || res->rcode || !res->data) { 5568ed2b524SDag-Erling Smørgrav if(verb) printf("resolve %s %s: no result\n", host, 5578ed2b524SDag-Erling Smørgrav (tp==LDNS_RR_TYPE_A)?"A":"AAAA"); 5588ed2b524SDag-Erling Smørgrav return; 5598ed2b524SDag-Erling Smørgrav } 560b7579f77SDag-Erling Smørgrav for(i = 0; res->data[i]; i++) { 561b7579f77SDag-Erling Smørgrav struct ip_list* ip = RR_to_ip(tp, res->data[i], res->len[i], 562b7579f77SDag-Erling Smørgrav port); 563b7579f77SDag-Erling Smørgrav if(!ip) continue; 564b7579f77SDag-Erling Smørgrav ip->next = *head; 565b7579f77SDag-Erling Smørgrav *head = ip; 566b7579f77SDag-Erling Smørgrav } 567b7579f77SDag-Erling Smørgrav ub_resolve_free(res); 568b7579f77SDag-Erling Smørgrav } 569b7579f77SDag-Erling Smørgrav 570b7579f77SDag-Erling Smørgrav /** parse a text IP address into a sockaddr */ 571b7579f77SDag-Erling Smørgrav static struct ip_list* 572ebc5657fSDag-Erling Smørgrav parse_ip_addr(const char* str, int port) 573b7579f77SDag-Erling Smørgrav { 574b7579f77SDag-Erling Smørgrav socklen_t len = 0; 575ebc5657fSDag-Erling Smørgrav union { 576b7579f77SDag-Erling Smørgrav struct sockaddr_in6 a6; 577b7579f77SDag-Erling Smørgrav struct sockaddr_in a; 578ebc5657fSDag-Erling Smørgrav } addr; 579b7579f77SDag-Erling Smørgrav struct ip_list* ip; 580b7579f77SDag-Erling Smørgrav uint16_t p = (uint16_t)port; 581ebc5657fSDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 582b7579f77SDag-Erling Smørgrav 583ebc5657fSDag-Erling Smørgrav if(inet_pton(AF_INET6, str, &addr.a6.sin6_addr) > 0) { 584b7579f77SDag-Erling Smørgrav /* it is an IPv6 */ 585ebc5657fSDag-Erling Smørgrav addr.a6.sin6_family = AF_INET6; 586ebc5657fSDag-Erling Smørgrav addr.a6.sin6_port = (in_port_t)htons(p); 587ebc5657fSDag-Erling Smørgrav len = (socklen_t)sizeof(addr.a6); 588b7579f77SDag-Erling Smørgrav } 589ebc5657fSDag-Erling Smørgrav if(inet_pton(AF_INET, str, &addr.a.sin_addr) > 0) { 590b7579f77SDag-Erling Smørgrav /* it is an IPv4 */ 591ebc5657fSDag-Erling Smørgrav addr.a.sin_family = AF_INET; 592ebc5657fSDag-Erling Smørgrav addr.a.sin_port = (in_port_t)htons(p); 593b7579f77SDag-Erling Smørgrav len = (socklen_t)sizeof(struct sockaddr_in); 594b7579f77SDag-Erling Smørgrav } 595b7579f77SDag-Erling Smørgrav if(!len) return NULL; 596b7579f77SDag-Erling Smørgrav ip = (struct ip_list*)calloc(1, sizeof(*ip)); 597b7579f77SDag-Erling Smørgrav if(!ip) { 598b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 599b7579f77SDag-Erling Smørgrav exit(0); 600b7579f77SDag-Erling Smørgrav } 601b7579f77SDag-Erling Smørgrav ip->len = len; 602ebc5657fSDag-Erling Smørgrav memmove(&ip->addr, &addr, len); 603b7579f77SDag-Erling Smørgrav if(verb) printf("server address is %s\n", str); 604b7579f77SDag-Erling Smørgrav return ip; 605b7579f77SDag-Erling Smørgrav } 606b7579f77SDag-Erling Smørgrav 607b7579f77SDag-Erling Smørgrav /** 608b7579f77SDag-Erling Smørgrav * Resolve a domain name (even though the resolver is down and there is 609b7579f77SDag-Erling Smørgrav * no trust anchor). Without DNSSEC validation. 610b7579f77SDag-Erling Smørgrav * @param host: the name to resolve. 611b7579f77SDag-Erling Smørgrav * If this name is an IP4 or IP6 address this address is returned. 612b7579f77SDag-Erling Smørgrav * @param port: the port number used for the returned IP structs. 613b7579f77SDag-Erling Smørgrav * @param res_conf: resolv.conf (if any). 614b7579f77SDag-Erling Smørgrav * @param root_hints: root hints (if any). 615b7579f77SDag-Erling Smørgrav * @param debugconf: unbound.conf for debugging options. 616b7579f77SDag-Erling Smørgrav * @param ip4only: use only ip4 for resolve and only lookup A 617b7579f77SDag-Erling Smørgrav * @param ip6only: use only ip6 for resolve and only lookup AAAA 618b7579f77SDag-Erling Smørgrav * default is to lookup A and AAAA using ip4 and ip6. 619b7579f77SDag-Erling Smørgrav * @return list of IP addresses. 620b7579f77SDag-Erling Smørgrav */ 621b7579f77SDag-Erling Smørgrav static struct ip_list* 622ebc5657fSDag-Erling Smørgrav resolve_name(const char* host, int port, const char* res_conf, 623ebc5657fSDag-Erling Smørgrav const char* root_hints, const char* debugconf, int ip4only, int ip6only) 624b7579f77SDag-Erling Smørgrav { 625b7579f77SDag-Erling Smørgrav struct ub_ctx* ctx; 626b7579f77SDag-Erling Smørgrav struct ip_list* list = NULL; 627b7579f77SDag-Erling Smørgrav /* first see if name is an IP address itself */ 628b7579f77SDag-Erling Smørgrav if( (list=parse_ip_addr(host, port)) ) { 629b7579f77SDag-Erling Smørgrav return list; 630b7579f77SDag-Erling Smørgrav } 631b7579f77SDag-Erling Smørgrav 632b7579f77SDag-Erling Smørgrav /* create resolver context */ 633b7579f77SDag-Erling Smørgrav ctx = create_unbound_context(res_conf, root_hints, debugconf, 634b7579f77SDag-Erling Smørgrav ip4only, ip6only); 635b7579f77SDag-Erling Smørgrav 636b7579f77SDag-Erling Smørgrav /* try resolution of A */ 637b7579f77SDag-Erling Smørgrav if(!ip6only) { 638b7579f77SDag-Erling Smørgrav resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_A, 639b7579f77SDag-Erling Smørgrav LDNS_RR_CLASS_IN, &list); 640b7579f77SDag-Erling Smørgrav } 641b7579f77SDag-Erling Smørgrav 642b7579f77SDag-Erling Smørgrav /* try resolution of AAAA */ 643b7579f77SDag-Erling Smørgrav if(!ip4only) { 644b7579f77SDag-Erling Smørgrav resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_AAAA, 645b7579f77SDag-Erling Smørgrav LDNS_RR_CLASS_IN, &list); 646b7579f77SDag-Erling Smørgrav } 647b7579f77SDag-Erling Smørgrav 648b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 649b7579f77SDag-Erling Smørgrav if(!list) { 650b7579f77SDag-Erling Smørgrav if(verb) printf("%s has no IP addresses I can use\n", host); 651b7579f77SDag-Erling Smørgrav exit(0); 652b7579f77SDag-Erling Smørgrav } 653b7579f77SDag-Erling Smørgrav return list; 654b7579f77SDag-Erling Smørgrav } 655b7579f77SDag-Erling Smørgrav 656b7579f77SDag-Erling Smørgrav /** clear used flags */ 657b7579f77SDag-Erling Smørgrav static void 658b7579f77SDag-Erling Smørgrav wipe_ip_usage(struct ip_list* p) 659b7579f77SDag-Erling Smørgrav { 660b7579f77SDag-Erling Smørgrav while(p) { 661b7579f77SDag-Erling Smørgrav p->used = 0; 662b7579f77SDag-Erling Smørgrav p = p->next; 663b7579f77SDag-Erling Smørgrav } 664b7579f77SDag-Erling Smørgrav } 665b7579f77SDag-Erling Smørgrav 6668a384985SDag-Erling Smørgrav /** count unused IPs */ 667b7579f77SDag-Erling Smørgrav static int 668b7579f77SDag-Erling Smørgrav count_unused(struct ip_list* p) 669b7579f77SDag-Erling Smørgrav { 670b7579f77SDag-Erling Smørgrav int num = 0; 671b7579f77SDag-Erling Smørgrav while(p) { 672b7579f77SDag-Erling Smørgrav if(!p->used) num++; 673b7579f77SDag-Erling Smørgrav p = p->next; 674b7579f77SDag-Erling Smørgrav } 675b7579f77SDag-Erling Smørgrav return num; 676b7579f77SDag-Erling Smørgrav } 677b7579f77SDag-Erling Smørgrav 678b7579f77SDag-Erling Smørgrav /** pick random unused element from IP list */ 679b7579f77SDag-Erling Smørgrav static struct ip_list* 680b7579f77SDag-Erling Smørgrav pick_random_ip(struct ip_list* list) 681b7579f77SDag-Erling Smørgrav { 682b7579f77SDag-Erling Smørgrav struct ip_list* p = list; 683b7579f77SDag-Erling Smørgrav int num = count_unused(list); 684b7579f77SDag-Erling Smørgrav int sel; 685b7579f77SDag-Erling Smørgrav if(num == 0) return NULL; 686b7579f77SDag-Erling Smørgrav /* not perfect, but random enough */ 687ff825849SDag-Erling Smørgrav sel = (int)arc4random_uniform((uint32_t)num); 688b7579f77SDag-Erling Smørgrav /* skip over unused elements that we did not select */ 689b7579f77SDag-Erling Smørgrav while(sel > 0 && p) { 690b7579f77SDag-Erling Smørgrav if(!p->used) sel--; 691b7579f77SDag-Erling Smørgrav p = p->next; 692b7579f77SDag-Erling Smørgrav } 693b7579f77SDag-Erling Smørgrav /* find the next unused element */ 694b7579f77SDag-Erling Smørgrav while(p && p->used) 695b7579f77SDag-Erling Smørgrav p = p->next; 696b7579f77SDag-Erling Smørgrav if(!p) return NULL; /* robustness */ 697b7579f77SDag-Erling Smørgrav return p; 698b7579f77SDag-Erling Smørgrav } 699b7579f77SDag-Erling Smørgrav 700b7579f77SDag-Erling Smørgrav /** close the fd */ 701b7579f77SDag-Erling Smørgrav static void 702b7579f77SDag-Erling Smørgrav fd_close(int fd) 703b7579f77SDag-Erling Smørgrav { 704b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK 705b7579f77SDag-Erling Smørgrav close(fd); 706b7579f77SDag-Erling Smørgrav #else 707b7579f77SDag-Erling Smørgrav closesocket(fd); 708b7579f77SDag-Erling Smørgrav #endif 709b7579f77SDag-Erling Smørgrav } 710b7579f77SDag-Erling Smørgrav 711b7579f77SDag-Erling Smørgrav /** printout socket errno */ 712b7579f77SDag-Erling Smørgrav static void 713b7579f77SDag-Erling Smørgrav print_sock_err(const char* msg) 714b7579f77SDag-Erling Smørgrav { 715b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK 716b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", msg, strerror(errno)); 717b7579f77SDag-Erling Smørgrav #else 718b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", msg, wsa_strerror(WSAGetLastError())); 719b7579f77SDag-Erling Smørgrav #endif 720b7579f77SDag-Erling Smørgrav } 721b7579f77SDag-Erling Smørgrav 722b7579f77SDag-Erling Smørgrav /** connect to IP address */ 723b7579f77SDag-Erling Smørgrav static int 724b7579f77SDag-Erling Smørgrav connect_to_ip(struct ip_list* ip) 725b7579f77SDag-Erling Smørgrav { 726b7579f77SDag-Erling Smørgrav int fd; 727b7579f77SDag-Erling Smørgrav verb_addr("connect to", ip); 728b7579f77SDag-Erling Smørgrav fd = socket(ip->len==(socklen_t)sizeof(struct sockaddr_in)? 729b7579f77SDag-Erling Smørgrav AF_INET:AF_INET6, SOCK_STREAM, 0); 730b7579f77SDag-Erling Smørgrav if(fd == -1) { 731b7579f77SDag-Erling Smørgrav print_sock_err("socket"); 732b7579f77SDag-Erling Smørgrav return -1; 733b7579f77SDag-Erling Smørgrav } 734b7579f77SDag-Erling Smørgrav if(connect(fd, (struct sockaddr*)&ip->addr, ip->len) < 0) { 735b7579f77SDag-Erling Smørgrav print_sock_err("connect"); 736b7579f77SDag-Erling Smørgrav fd_close(fd); 737b7579f77SDag-Erling Smørgrav return -1; 738b7579f77SDag-Erling Smørgrav } 739b7579f77SDag-Erling Smørgrav return fd; 740b7579f77SDag-Erling Smørgrav } 741b7579f77SDag-Erling Smørgrav 742b7579f77SDag-Erling Smørgrav /** create SSL context */ 743b7579f77SDag-Erling Smørgrav static SSL_CTX* 744b7579f77SDag-Erling Smørgrav setup_sslctx(void) 745b7579f77SDag-Erling Smørgrav { 746b7579f77SDag-Erling Smørgrav SSL_CTX* sslctx = SSL_CTX_new(SSLv23_client_method()); 747b7579f77SDag-Erling Smørgrav if(!sslctx) { 748b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_CTX_new error\n"); 749b7579f77SDag-Erling Smørgrav return NULL; 750b7579f77SDag-Erling Smørgrav } 751b7579f77SDag-Erling Smørgrav return sslctx; 752b7579f77SDag-Erling Smørgrav } 753b7579f77SDag-Erling Smørgrav 754b7579f77SDag-Erling Smørgrav /** initiate TLS on a connection */ 755b7579f77SDag-Erling Smørgrav static SSL* 756b7579f77SDag-Erling Smørgrav TLS_initiate(SSL_CTX* sslctx, int fd) 757b7579f77SDag-Erling Smørgrav { 758b7579f77SDag-Erling Smørgrav X509* x; 759b7579f77SDag-Erling Smørgrav int r; 760b7579f77SDag-Erling Smørgrav SSL* ssl = SSL_new(sslctx); 761b7579f77SDag-Erling Smørgrav if(!ssl) { 762b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_new error\n"); 763b7579f77SDag-Erling Smørgrav return NULL; 764b7579f77SDag-Erling Smørgrav } 765b7579f77SDag-Erling Smørgrav SSL_set_connect_state(ssl); 766b7579f77SDag-Erling Smørgrav (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 767b7579f77SDag-Erling Smørgrav if(!SSL_set_fd(ssl, fd)) { 768b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_set_fd error\n"); 769b7579f77SDag-Erling Smørgrav SSL_free(ssl); 770b7579f77SDag-Erling Smørgrav return NULL; 771b7579f77SDag-Erling Smørgrav } 772b7579f77SDag-Erling Smørgrav while(1) { 773b7579f77SDag-Erling Smørgrav ERR_clear_error(); 774b7579f77SDag-Erling Smørgrav if( (r=SSL_do_handshake(ssl)) == 1) 775b7579f77SDag-Erling Smørgrav break; 776b7579f77SDag-Erling Smørgrav r = SSL_get_error(ssl, r); 777b7579f77SDag-Erling Smørgrav if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { 778b7579f77SDag-Erling Smørgrav if(verb) printf("SSL handshake failed\n"); 779b7579f77SDag-Erling Smørgrav SSL_free(ssl); 780b7579f77SDag-Erling Smørgrav return NULL; 781b7579f77SDag-Erling Smørgrav } 782b7579f77SDag-Erling Smørgrav /* wants to be called again */ 783b7579f77SDag-Erling Smørgrav } 784b7579f77SDag-Erling Smørgrav x = SSL_get_peer_certificate(ssl); 785b7579f77SDag-Erling Smørgrav if(!x) { 786b7579f77SDag-Erling Smørgrav if(verb) printf("Server presented no peer certificate\n"); 787b7579f77SDag-Erling Smørgrav SSL_free(ssl); 788b7579f77SDag-Erling Smørgrav return NULL; 789b7579f77SDag-Erling Smørgrav } 790b7579f77SDag-Erling Smørgrav verb_cert("server SSL certificate", x); 791b7579f77SDag-Erling Smørgrav X509_free(x); 792b7579f77SDag-Erling Smørgrav return ssl; 793b7579f77SDag-Erling Smørgrav } 794b7579f77SDag-Erling Smørgrav 795b7579f77SDag-Erling Smørgrav /** perform neat TLS shutdown */ 796b7579f77SDag-Erling Smørgrav static void 797b7579f77SDag-Erling Smørgrav TLS_shutdown(int fd, SSL* ssl, SSL_CTX* sslctx) 798b7579f77SDag-Erling Smørgrav { 799b7579f77SDag-Erling Smørgrav /* shutdown the SSL connection nicely */ 800b7579f77SDag-Erling Smørgrav if(SSL_shutdown(ssl) == 0) { 801b7579f77SDag-Erling Smørgrav SSL_shutdown(ssl); 802b7579f77SDag-Erling Smørgrav } 803b7579f77SDag-Erling Smørgrav SSL_free(ssl); 804b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 805b7579f77SDag-Erling Smørgrav fd_close(fd); 806b7579f77SDag-Erling Smørgrav } 807b7579f77SDag-Erling Smørgrav 808b7579f77SDag-Erling Smørgrav /** write a line over SSL */ 809b7579f77SDag-Erling Smørgrav static int 810ebc5657fSDag-Erling Smørgrav write_ssl_line(SSL* ssl, const char* str, const char* sec) 811b7579f77SDag-Erling Smørgrav { 812b7579f77SDag-Erling Smørgrav char buf[1024]; 813b7579f77SDag-Erling Smørgrav size_t l; 814b7579f77SDag-Erling Smørgrav if(sec) { 815b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), str, sec); 816b7579f77SDag-Erling Smørgrav } else { 817b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), "%s", str); 818b7579f77SDag-Erling Smørgrav } 819b7579f77SDag-Erling Smørgrav l = strlen(buf); 820b7579f77SDag-Erling Smørgrav if(l+2 >= sizeof(buf)) { 821b7579f77SDag-Erling Smørgrav if(verb) printf("line too long\n"); 822b7579f77SDag-Erling Smørgrav return 0; 823b7579f77SDag-Erling Smørgrav } 824b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("SSL_write: %s\n", buf); 825b7579f77SDag-Erling Smørgrav buf[l] = '\r'; 826b7579f77SDag-Erling Smørgrav buf[l+1] = '\n'; 827b7579f77SDag-Erling Smørgrav buf[l+2] = 0; 828b7579f77SDag-Erling Smørgrav /* add \r\n */ 829b7579f77SDag-Erling Smørgrav if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) { 830b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_write %s", str); 831b7579f77SDag-Erling Smørgrav return 0; 832b7579f77SDag-Erling Smørgrav } 833b7579f77SDag-Erling Smørgrav return 1; 834b7579f77SDag-Erling Smørgrav } 835b7579f77SDag-Erling Smørgrav 836b7579f77SDag-Erling Smørgrav /** process header line, check rcode and keeping track of size */ 837b7579f77SDag-Erling Smørgrav static int 838b7579f77SDag-Erling Smørgrav process_one_header(char* buf, size_t* clen, int* chunked) 839b7579f77SDag-Erling Smørgrav { 840b7579f77SDag-Erling Smørgrav if(verb>=2) printf("header: '%s'\n", buf); 841b7579f77SDag-Erling Smørgrav if(strncasecmp(buf, "HTTP/1.1 ", 9) == 0) { 842b7579f77SDag-Erling Smørgrav /* check returncode */ 843b7579f77SDag-Erling Smørgrav if(buf[9] != '2') { 844b7579f77SDag-Erling Smørgrav if(verb) printf("bad status %s\n", buf+9); 845b7579f77SDag-Erling Smørgrav return 0; 846b7579f77SDag-Erling Smørgrav } 847b7579f77SDag-Erling Smørgrav } else if(strncasecmp(buf, "Content-Length: ", 16) == 0) { 848b7579f77SDag-Erling Smørgrav if(!*chunked) 849b7579f77SDag-Erling Smørgrav *clen = (size_t)atoi(buf+16); 850b7579f77SDag-Erling Smørgrav } else if(strncasecmp(buf, "Transfer-Encoding: chunked", 19+7) == 0) { 851b7579f77SDag-Erling Smørgrav *clen = 0; 852b7579f77SDag-Erling Smørgrav *chunked = 1; 853b7579f77SDag-Erling Smørgrav } 854b7579f77SDag-Erling Smørgrav return 1; 855b7579f77SDag-Erling Smørgrav } 856b7579f77SDag-Erling Smørgrav 857b7579f77SDag-Erling Smørgrav /** 858b7579f77SDag-Erling Smørgrav * Read one line from SSL 859b7579f77SDag-Erling Smørgrav * zero terminates. 860b7579f77SDag-Erling Smørgrav * skips "\r\n" (but not copied to buf). 861b7579f77SDag-Erling Smørgrav * @param ssl: the SSL connection to read from (blocking). 862b7579f77SDag-Erling Smørgrav * @param buf: buffer to return line in. 863b7579f77SDag-Erling Smørgrav * @param len: size of the buffer. 864b7579f77SDag-Erling Smørgrav * @return 0 on error, 1 on success. 865b7579f77SDag-Erling Smørgrav */ 866b7579f77SDag-Erling Smørgrav static int 867b7579f77SDag-Erling Smørgrav read_ssl_line(SSL* ssl, char* buf, size_t len) 868b7579f77SDag-Erling Smørgrav { 869b7579f77SDag-Erling Smørgrav size_t n = 0; 870b7579f77SDag-Erling Smørgrav int r; 871b7579f77SDag-Erling Smørgrav int endnl = 0; 872b7579f77SDag-Erling Smørgrav while(1) { 873b7579f77SDag-Erling Smørgrav if(n >= len) { 874b7579f77SDag-Erling Smørgrav if(verb) printf("line too long\n"); 875b7579f77SDag-Erling Smørgrav return 0; 876b7579f77SDag-Erling Smørgrav } 877b7579f77SDag-Erling Smørgrav if((r = SSL_read(ssl, buf+n, 1)) <= 0) { 878b7579f77SDag-Erling Smørgrav if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 879b7579f77SDag-Erling Smørgrav /* EOF */ 880b7579f77SDag-Erling Smørgrav break; 881b7579f77SDag-Erling Smørgrav } 882b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read\n"); 883b7579f77SDag-Erling Smørgrav return 0; 884b7579f77SDag-Erling Smørgrav } 885b7579f77SDag-Erling Smørgrav if(endnl && buf[n] == '\n') { 886b7579f77SDag-Erling Smørgrav break; 887b7579f77SDag-Erling Smørgrav } else if(endnl) { 888b7579f77SDag-Erling Smørgrav /* bad data */ 889b7579f77SDag-Erling Smørgrav if(verb) printf("error: stray linefeeds\n"); 890b7579f77SDag-Erling Smørgrav return 0; 891b7579f77SDag-Erling Smørgrav } else if(buf[n] == '\r') { 892b7579f77SDag-Erling Smørgrav /* skip \r, and also \n on the wire */ 893b7579f77SDag-Erling Smørgrav endnl = 1; 894b7579f77SDag-Erling Smørgrav continue; 895b7579f77SDag-Erling Smørgrav } else if(buf[n] == '\n') { 896b7579f77SDag-Erling Smørgrav /* skip the \n, we are done */ 897b7579f77SDag-Erling Smørgrav break; 898b7579f77SDag-Erling Smørgrav } else n++; 899b7579f77SDag-Erling Smørgrav } 900b7579f77SDag-Erling Smørgrav buf[n] = 0; 901b7579f77SDag-Erling Smørgrav return 1; 902b7579f77SDag-Erling Smørgrav } 903b7579f77SDag-Erling Smørgrav 904b7579f77SDag-Erling Smørgrav /** read http headers and process them */ 905b7579f77SDag-Erling Smørgrav static size_t 906b7579f77SDag-Erling Smørgrav read_http_headers(SSL* ssl, size_t* clen) 907b7579f77SDag-Erling Smørgrav { 908b7579f77SDag-Erling Smørgrav char buf[1024]; 909b7579f77SDag-Erling Smørgrav int chunked = 0; 910b7579f77SDag-Erling Smørgrav *clen = 0; 911b7579f77SDag-Erling Smørgrav while(read_ssl_line(ssl, buf, sizeof(buf))) { 912b7579f77SDag-Erling Smørgrav if(buf[0] == 0) 913b7579f77SDag-Erling Smørgrav return 1; 914b7579f77SDag-Erling Smørgrav if(!process_one_header(buf, clen, &chunked)) 915b7579f77SDag-Erling Smørgrav return 0; 916b7579f77SDag-Erling Smørgrav } 917b7579f77SDag-Erling Smørgrav return 0; 918b7579f77SDag-Erling Smørgrav } 919b7579f77SDag-Erling Smørgrav 920b7579f77SDag-Erling Smørgrav /** read a data chunk */ 921b7579f77SDag-Erling Smørgrav static char* 922b7579f77SDag-Erling Smørgrav read_data_chunk(SSL* ssl, size_t len) 923b7579f77SDag-Erling Smørgrav { 924b7579f77SDag-Erling Smørgrav size_t got = 0; 925b7579f77SDag-Erling Smørgrav int r; 92609a3aaf3SDag-Erling Smørgrav char* data; 92709a3aaf3SDag-Erling Smørgrav if(len >= 0xfffffff0) 92809a3aaf3SDag-Erling Smørgrav return NULL; /* to protect against integer overflow in malloc*/ 92909a3aaf3SDag-Erling Smørgrav data = malloc(len+1); 930b7579f77SDag-Erling Smørgrav if(!data) { 931b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 932b7579f77SDag-Erling Smørgrav return NULL; 933b7579f77SDag-Erling Smørgrav } 934b7579f77SDag-Erling Smørgrav while(got < len) { 935b7579f77SDag-Erling Smørgrav if((r = SSL_read(ssl, data+got, (int)(len-got))) <= 0) { 936b7579f77SDag-Erling Smørgrav if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 937b7579f77SDag-Erling Smørgrav /* EOF */ 938b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read: unexpected EOF\n"); 939b7579f77SDag-Erling Smørgrav free(data); 940b7579f77SDag-Erling Smørgrav return NULL; 941b7579f77SDag-Erling Smørgrav } 942b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read\n"); 943b7579f77SDag-Erling Smørgrav free(data); 944b7579f77SDag-Erling Smørgrav return NULL; 945b7579f77SDag-Erling Smørgrav } 946b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("at %d/%d\n", (int)got, (int)len); 947b7579f77SDag-Erling Smørgrav got += r; 948b7579f77SDag-Erling Smørgrav } 949b7579f77SDag-Erling Smørgrav if(verb>=2) printf("read %d data\n", (int)len); 950b7579f77SDag-Erling Smørgrav data[len] = 0; 951b7579f77SDag-Erling Smørgrav return data; 952b7579f77SDag-Erling Smørgrav } 953b7579f77SDag-Erling Smørgrav 954b7579f77SDag-Erling Smørgrav /** parse chunk header */ 955b7579f77SDag-Erling Smørgrav static int 956b7579f77SDag-Erling Smørgrav parse_chunk_header(char* buf, size_t* result) 957b7579f77SDag-Erling Smørgrav { 958b7579f77SDag-Erling Smørgrav char* e = NULL; 959b7579f77SDag-Erling Smørgrav size_t v = (size_t)strtol(buf, &e, 16); 960b7579f77SDag-Erling Smørgrav if(e == buf) 961b7579f77SDag-Erling Smørgrav return 0; 962b7579f77SDag-Erling Smørgrav *result = v; 963b7579f77SDag-Erling Smørgrav return 1; 964b7579f77SDag-Erling Smørgrav } 965b7579f77SDag-Erling Smørgrav 966b7579f77SDag-Erling Smørgrav /** read chunked data from connection */ 967b7579f77SDag-Erling Smørgrav static BIO* 968b7579f77SDag-Erling Smørgrav do_chunked_read(SSL* ssl) 969b7579f77SDag-Erling Smørgrav { 970b7579f77SDag-Erling Smørgrav char buf[1024]; 971b7579f77SDag-Erling Smørgrav size_t len; 972b7579f77SDag-Erling Smørgrav char* body; 973b7579f77SDag-Erling Smørgrav BIO* mem = BIO_new(BIO_s_mem()); 974b7579f77SDag-Erling Smørgrav if(verb>=3) printf("do_chunked_read\n"); 975b7579f77SDag-Erling Smørgrav if(!mem) { 976b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 977b7579f77SDag-Erling Smørgrav return NULL; 978b7579f77SDag-Erling Smørgrav } 979b7579f77SDag-Erling Smørgrav while(read_ssl_line(ssl, buf, sizeof(buf))) { 980b7579f77SDag-Erling Smørgrav /* read the chunked start line */ 981b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunk header: %s\n", buf); 982b7579f77SDag-Erling Smørgrav if(!parse_chunk_header(buf, &len)) { 983b7579f77SDag-Erling Smørgrav BIO_free(mem); 984b7579f77SDag-Erling Smørgrav if(verb>=3) printf("could not parse chunk header\n"); 985b7579f77SDag-Erling Smørgrav return NULL; 986b7579f77SDag-Erling Smørgrav } 987b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunk len: %d\n", (int)len); 988b7579f77SDag-Erling Smørgrav /* are we done? */ 989b7579f77SDag-Erling Smørgrav if(len == 0) { 990b7579f77SDag-Erling Smørgrav char z = 0; 991b7579f77SDag-Erling Smørgrav /* skip end-of-chunk-trailer lines, 992b7579f77SDag-Erling Smørgrav * until the empty line after that */ 993b7579f77SDag-Erling Smørgrav do { 994b7579f77SDag-Erling Smørgrav if(!read_ssl_line(ssl, buf, sizeof(buf))) { 995b7579f77SDag-Erling Smørgrav BIO_free(mem); 996b7579f77SDag-Erling Smørgrav return NULL; 997b7579f77SDag-Erling Smørgrav } 998b7579f77SDag-Erling Smørgrav } while (strlen(buf) > 0); 999b7579f77SDag-Erling Smørgrav /* end of chunks, zero terminate it */ 1000b7579f77SDag-Erling Smørgrav if(BIO_write(mem, &z, 1) <= 0) { 1001b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1002b7579f77SDag-Erling Smørgrav BIO_free(mem); 1003b7579f77SDag-Erling Smørgrav return NULL; 1004b7579f77SDag-Erling Smørgrav } 1005b7579f77SDag-Erling Smørgrav return mem; 1006b7579f77SDag-Erling Smørgrav } 1007b7579f77SDag-Erling Smørgrav /* read the chunked body */ 1008b7579f77SDag-Erling Smørgrav body = read_data_chunk(ssl, len); 1009b7579f77SDag-Erling Smørgrav if(!body) { 1010b7579f77SDag-Erling Smørgrav BIO_free(mem); 1011b7579f77SDag-Erling Smørgrav return NULL; 1012b7579f77SDag-Erling Smørgrav } 1013b7579f77SDag-Erling Smørgrav if(BIO_write(mem, body, (int)len) <= 0) { 1014b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1015b7579f77SDag-Erling Smørgrav free(body); 1016b7579f77SDag-Erling Smørgrav BIO_free(mem); 1017b7579f77SDag-Erling Smørgrav return NULL; 1018b7579f77SDag-Erling Smørgrav } 1019b7579f77SDag-Erling Smørgrav free(body); 1020b7579f77SDag-Erling Smørgrav /* skip empty line after data chunk */ 1021b7579f77SDag-Erling Smørgrav if(!read_ssl_line(ssl, buf, sizeof(buf))) { 1022b7579f77SDag-Erling Smørgrav BIO_free(mem); 1023b7579f77SDag-Erling Smørgrav return NULL; 1024b7579f77SDag-Erling Smørgrav } 1025b7579f77SDag-Erling Smørgrav } 1026b7579f77SDag-Erling Smørgrav BIO_free(mem); 1027b7579f77SDag-Erling Smørgrav return NULL; 1028b7579f77SDag-Erling Smørgrav } 1029b7579f77SDag-Erling Smørgrav 1030b7579f77SDag-Erling Smørgrav /** start HTTP1.1 transaction on SSL */ 1031b7579f77SDag-Erling Smørgrav static int 1032ebc5657fSDag-Erling Smørgrav write_http_get(SSL* ssl, const char* pathname, const char* urlname) 1033b7579f77SDag-Erling Smørgrav { 1034b7579f77SDag-Erling Smørgrav if(write_ssl_line(ssl, "GET /%s HTTP/1.1", pathname) && 1035b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "Host: %s", urlname) && 1036b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "User-Agent: unbound-anchor/%s", 1037b7579f77SDag-Erling Smørgrav PACKAGE_VERSION) && 1038b7579f77SDag-Erling Smørgrav /* We do not really do multiple queries per connection, 1039b7579f77SDag-Erling Smørgrav * but this header setting is also not needed. 1040b7579f77SDag-Erling Smørgrav * write_ssl_line(ssl, "Connection: close", NULL) &&*/ 1041b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "", NULL)) { 1042b7579f77SDag-Erling Smørgrav return 1; 1043b7579f77SDag-Erling Smørgrav } 1044b7579f77SDag-Erling Smørgrav return 0; 1045b7579f77SDag-Erling Smørgrav } 1046b7579f77SDag-Erling Smørgrav 1047b7579f77SDag-Erling Smørgrav /** read chunked data and zero terminate; len is without zero */ 1048b7579f77SDag-Erling Smørgrav static char* 1049b7579f77SDag-Erling Smørgrav read_chunked_zero_terminate(SSL* ssl, size_t* len) 1050b7579f77SDag-Erling Smørgrav { 1051b7579f77SDag-Erling Smørgrav /* do the chunked version */ 1052b7579f77SDag-Erling Smørgrav BIO* tmp = do_chunked_read(ssl); 1053b7579f77SDag-Erling Smørgrav char* data, *d = NULL; 1054b7579f77SDag-Erling Smørgrav size_t l; 1055b7579f77SDag-Erling Smørgrav if(!tmp) { 1056b7579f77SDag-Erling Smørgrav if(verb) printf("could not read from https\n"); 1057b7579f77SDag-Erling Smørgrav return NULL; 1058b7579f77SDag-Erling Smørgrav } 1059b7579f77SDag-Erling Smørgrav l = (size_t)BIO_get_mem_data(tmp, &d); 1060b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunked data is %d\n", (int)l); 1061b7579f77SDag-Erling Smørgrav if(l == 0 || d == NULL) { 1062b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1063b7579f77SDag-Erling Smørgrav return NULL; 1064b7579f77SDag-Erling Smørgrav } 1065b7579f77SDag-Erling Smørgrav *len = l-1; 1066b7579f77SDag-Erling Smørgrav data = (char*)malloc(l); 1067b7579f77SDag-Erling Smørgrav if(data == NULL) { 1068b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1069b7579f77SDag-Erling Smørgrav return NULL; 1070b7579f77SDag-Erling Smørgrav } 1071b7579f77SDag-Erling Smørgrav memcpy(data, d, l); 1072b7579f77SDag-Erling Smørgrav BIO_free(tmp); 1073b7579f77SDag-Erling Smørgrav return data; 1074b7579f77SDag-Erling Smørgrav } 1075b7579f77SDag-Erling Smørgrav 1076b7579f77SDag-Erling Smørgrav /** read HTTP result from SSL */ 1077b7579f77SDag-Erling Smørgrav static BIO* 1078b7579f77SDag-Erling Smørgrav read_http_result(SSL* ssl) 1079b7579f77SDag-Erling Smørgrav { 1080b7579f77SDag-Erling Smørgrav size_t len = 0; 1081b7579f77SDag-Erling Smørgrav char* data; 1082b7579f77SDag-Erling Smørgrav BIO* m; 1083b7579f77SDag-Erling Smørgrav if(!read_http_headers(ssl, &len)) { 1084b7579f77SDag-Erling Smørgrav return NULL; 1085b7579f77SDag-Erling Smørgrav } 1086b7579f77SDag-Erling Smørgrav if(len == 0) { 1087b7579f77SDag-Erling Smørgrav data = read_chunked_zero_terminate(ssl, &len); 1088b7579f77SDag-Erling Smørgrav } else { 1089b7579f77SDag-Erling Smørgrav data = read_data_chunk(ssl, len); 1090b7579f77SDag-Erling Smørgrav } 1091b7579f77SDag-Erling Smørgrav if(!data) return NULL; 1092b7579f77SDag-Erling Smørgrav if(verb >= 4) print_data("read data", data, (int)len); 1093b7579f77SDag-Erling Smørgrav m = BIO_new_mem_buf(data, (int)len); 1094b7579f77SDag-Erling Smørgrav if(!m) { 1095b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1096b7579f77SDag-Erling Smørgrav exit(0); 1097b7579f77SDag-Erling Smørgrav } 1098b7579f77SDag-Erling Smørgrav return m; 1099b7579f77SDag-Erling Smørgrav } 1100b7579f77SDag-Erling Smørgrav 1101b7579f77SDag-Erling Smørgrav /** https to an IP addr, return BIO with pathname or NULL */ 1102b7579f77SDag-Erling Smørgrav static BIO* 1103ebc5657fSDag-Erling Smørgrav https_to_ip(struct ip_list* ip, const char* pathname, const char* urlname) 1104b7579f77SDag-Erling Smørgrav { 1105b7579f77SDag-Erling Smørgrav int fd; 1106b7579f77SDag-Erling Smørgrav SSL* ssl; 1107b7579f77SDag-Erling Smørgrav BIO* bio; 1108b7579f77SDag-Erling Smørgrav SSL_CTX* sslctx = setup_sslctx(); 1109b7579f77SDag-Erling Smørgrav if(!sslctx) { 1110b7579f77SDag-Erling Smørgrav return NULL; 1111b7579f77SDag-Erling Smørgrav } 1112b7579f77SDag-Erling Smørgrav fd = connect_to_ip(ip); 1113b7579f77SDag-Erling Smørgrav if(fd == -1) { 1114b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1115b7579f77SDag-Erling Smørgrav return NULL; 1116b7579f77SDag-Erling Smørgrav } 1117b7579f77SDag-Erling Smørgrav ssl = TLS_initiate(sslctx, fd); 1118b7579f77SDag-Erling Smørgrav if(!ssl) { 1119b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1120b7579f77SDag-Erling Smørgrav fd_close(fd); 1121b7579f77SDag-Erling Smørgrav return NULL; 1122b7579f77SDag-Erling Smørgrav } 1123b7579f77SDag-Erling Smørgrav if(!write_http_get(ssl, pathname, urlname)) { 1124b7579f77SDag-Erling Smørgrav if(verb) printf("could not write to server\n"); 1125b7579f77SDag-Erling Smørgrav SSL_free(ssl); 1126b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1127b7579f77SDag-Erling Smørgrav fd_close(fd); 1128b7579f77SDag-Erling Smørgrav return NULL; 1129b7579f77SDag-Erling Smørgrav } 1130b7579f77SDag-Erling Smørgrav bio = read_http_result(ssl); 1131b7579f77SDag-Erling Smørgrav TLS_shutdown(fd, ssl, sslctx); 1132b7579f77SDag-Erling Smørgrav return bio; 1133b7579f77SDag-Erling Smørgrav } 1134b7579f77SDag-Erling Smørgrav 1135b7579f77SDag-Erling Smørgrav /** 1136b7579f77SDag-Erling Smørgrav * Do a HTTPS, HTTP1.1 over TLS, to fetch a file 1137b7579f77SDag-Erling Smørgrav * @param ip_list: list of IP addresses to use to fetch from. 1138b7579f77SDag-Erling Smørgrav * @param pathname: pathname of file on server to GET. 1139b7579f77SDag-Erling Smørgrav * @param urlname: name to pass as the virtual host for this request. 1140b7579f77SDag-Erling Smørgrav * @return a memory BIO with the file in it. 1141b7579f77SDag-Erling Smørgrav */ 1142b7579f77SDag-Erling Smørgrav static BIO* 1143ebc5657fSDag-Erling Smørgrav https(struct ip_list* ip_list, const char* pathname, const char* urlname) 1144b7579f77SDag-Erling Smørgrav { 1145b7579f77SDag-Erling Smørgrav struct ip_list* ip; 1146b7579f77SDag-Erling Smørgrav BIO* bio = NULL; 1147b7579f77SDag-Erling Smørgrav /* try random address first, and work through the list */ 1148b7579f77SDag-Erling Smørgrav wipe_ip_usage(ip_list); 1149b7579f77SDag-Erling Smørgrav while( (ip = pick_random_ip(ip_list)) ) { 1150b7579f77SDag-Erling Smørgrav ip->used = 1; 1151b7579f77SDag-Erling Smørgrav bio = https_to_ip(ip, pathname, urlname); 1152b7579f77SDag-Erling Smørgrav if(bio) break; 1153b7579f77SDag-Erling Smørgrav } 1154b7579f77SDag-Erling Smørgrav if(!bio) { 1155b7579f77SDag-Erling Smørgrav if(verb) printf("could not fetch %s\n", pathname); 1156b7579f77SDag-Erling Smørgrav exit(0); 1157b7579f77SDag-Erling Smørgrav } else { 1158b7579f77SDag-Erling Smørgrav if(verb) printf("fetched %s (%d bytes)\n", 1159b7579f77SDag-Erling Smørgrav pathname, (int)BIO_ctrl_pending(bio)); 1160b7579f77SDag-Erling Smørgrav } 1161b7579f77SDag-Erling Smørgrav return bio; 1162b7579f77SDag-Erling Smørgrav } 1163b7579f77SDag-Erling Smørgrav 1164b7579f77SDag-Erling Smørgrav /** free up a downloaded file BIO */ 1165b7579f77SDag-Erling Smørgrav static void 1166b7579f77SDag-Erling Smørgrav free_file_bio(BIO* bio) 1167b7579f77SDag-Erling Smørgrav { 1168b7579f77SDag-Erling Smørgrav char* pp = NULL; 1169b7579f77SDag-Erling Smørgrav (void)BIO_reset(bio); 1170b7579f77SDag-Erling Smørgrav (void)BIO_get_mem_data(bio, &pp); 1171b7579f77SDag-Erling Smørgrav free(pp); 1172b7579f77SDag-Erling Smørgrav BIO_free(bio); 1173b7579f77SDag-Erling Smørgrav } 1174b7579f77SDag-Erling Smørgrav 1175b7579f77SDag-Erling Smørgrav /** XML parse private data during the parse */ 1176b7579f77SDag-Erling Smørgrav struct xml_data { 1177b7579f77SDag-Erling Smørgrav /** the parser, reference */ 1178b7579f77SDag-Erling Smørgrav XML_Parser parser; 1179b7579f77SDag-Erling Smørgrav /** the current tag; malloced; or NULL outside of tags */ 1180b7579f77SDag-Erling Smørgrav char* tag; 1181b7579f77SDag-Erling Smørgrav /** current date to use during the parse */ 1182b7579f77SDag-Erling Smørgrav time_t date; 1183b7579f77SDag-Erling Smørgrav /** number of keys usefully read in */ 1184b7579f77SDag-Erling Smørgrav int num_keys; 1185b7579f77SDag-Erling Smørgrav /** the compiled anchors as DS records */ 1186b7579f77SDag-Erling Smørgrav BIO* ds; 1187b7579f77SDag-Erling Smørgrav 1188b7579f77SDag-Erling Smørgrav /** do we want to use this anchor? */ 1189b7579f77SDag-Erling Smørgrav int use_key; 1190b7579f77SDag-Erling Smørgrav /** the current anchor: Zone */ 1191b7579f77SDag-Erling Smørgrav BIO* czone; 1192b7579f77SDag-Erling Smørgrav /** the current anchor: KeyTag */ 1193b7579f77SDag-Erling Smørgrav BIO* ctag; 1194b7579f77SDag-Erling Smørgrav /** the current anchor: Algorithm */ 1195b7579f77SDag-Erling Smørgrav BIO* calgo; 1196b7579f77SDag-Erling Smørgrav /** the current anchor: DigestType */ 1197b7579f77SDag-Erling Smørgrav BIO* cdigtype; 1198b7579f77SDag-Erling Smørgrav /** the current anchor: Digest*/ 1199b7579f77SDag-Erling Smørgrav BIO* cdigest; 1200b7579f77SDag-Erling Smørgrav }; 1201b7579f77SDag-Erling Smørgrav 1202b7579f77SDag-Erling Smørgrav /** The BIO for the tag */ 1203b7579f77SDag-Erling Smørgrav static BIO* 1204b7579f77SDag-Erling Smørgrav xml_selectbio(struct xml_data* data, const char* tag) 1205b7579f77SDag-Erling Smørgrav { 1206b7579f77SDag-Erling Smørgrav BIO* b = NULL; 1207b7579f77SDag-Erling Smørgrav if(strcasecmp(tag, "KeyTag") == 0) 1208b7579f77SDag-Erling Smørgrav b = data->ctag; 1209b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "Algorithm") == 0) 1210b7579f77SDag-Erling Smørgrav b = data->calgo; 1211b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "DigestType") == 0) 1212b7579f77SDag-Erling Smørgrav b = data->cdigtype; 1213b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "Digest") == 0) 1214b7579f77SDag-Erling Smørgrav b = data->cdigest; 1215b7579f77SDag-Erling Smørgrav return b; 1216b7579f77SDag-Erling Smørgrav } 1217b7579f77SDag-Erling Smørgrav 1218b7579f77SDag-Erling Smørgrav /** 1219b7579f77SDag-Erling Smørgrav * XML handle character data, the data inside an element. 1220b7579f77SDag-Erling Smørgrav * @param userData: xml_data structure 1221b7579f77SDag-Erling Smørgrav * @param s: the character data. May not all be in one callback. 1222b7579f77SDag-Erling Smørgrav * NOT zero terminated. 1223b7579f77SDag-Erling Smørgrav * @param len: length of this part of the data. 1224b7579f77SDag-Erling Smørgrav */ 1225ebc5657fSDag-Erling Smørgrav static void 1226b7579f77SDag-Erling Smørgrav xml_charhandle(void *userData, const XML_Char *s, int len) 1227b7579f77SDag-Erling Smørgrav { 1228b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1229b7579f77SDag-Erling Smørgrav BIO* b = NULL; 1230b7579f77SDag-Erling Smørgrav /* skip characters outside of elements */ 1231b7579f77SDag-Erling Smørgrav if(!data->tag) 1232b7579f77SDag-Erling Smørgrav return; 1233b7579f77SDag-Erling Smørgrav if(verb>=4) { 1234b7579f77SDag-Erling Smørgrav int i; 1235b7579f77SDag-Erling Smørgrav printf("%s%s charhandle: '", 1236b7579f77SDag-Erling Smørgrav data->use_key?"use ":"", 1237b7579f77SDag-Erling Smørgrav data->tag?data->tag:"none"); 1238b7579f77SDag-Erling Smørgrav for(i=0; i<len; i++) 1239b7579f77SDag-Erling Smørgrav printf("%c", s[i]); 1240b7579f77SDag-Erling Smørgrav printf("'\n"); 1241b7579f77SDag-Erling Smørgrav } 1242b7579f77SDag-Erling Smørgrav if(strcasecmp(data->tag, "Zone") == 0) { 124317d15b25SDag-Erling Smørgrav if(BIO_write(data->czone, s, len) < 0) { 1244b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1245b7579f77SDag-Erling Smørgrav exit(0); 1246b7579f77SDag-Erling Smørgrav } 1247b7579f77SDag-Erling Smørgrav return; 1248b7579f77SDag-Erling Smørgrav } 1249b7579f77SDag-Erling Smørgrav /* only store if key is used */ 1250b7579f77SDag-Erling Smørgrav if(!data->use_key) 1251b7579f77SDag-Erling Smørgrav return; 1252b7579f77SDag-Erling Smørgrav b = xml_selectbio(data, data->tag); 1253b7579f77SDag-Erling Smørgrav if(b) { 125417d15b25SDag-Erling Smørgrav if(BIO_write(b, s, len) < 0) { 1255b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1256b7579f77SDag-Erling Smørgrav exit(0); 1257b7579f77SDag-Erling Smørgrav } 1258b7579f77SDag-Erling Smørgrav } 1259b7579f77SDag-Erling Smørgrav } 1260b7579f77SDag-Erling Smørgrav 1261b7579f77SDag-Erling Smørgrav /** 1262b7579f77SDag-Erling Smørgrav * XML fetch value of particular attribute(by name) or NULL if not present. 1263b7579f77SDag-Erling Smørgrav * @param atts: attribute array (from xml_startelem). 1264b7579f77SDag-Erling Smørgrav * @param name: name of attribute to look for. 1265b7579f77SDag-Erling Smørgrav * @return the value or NULL. (ptr into atts). 1266b7579f77SDag-Erling Smørgrav */ 1267b7579f77SDag-Erling Smørgrav static const XML_Char* 1268ebc5657fSDag-Erling Smørgrav find_att(const XML_Char **atts, const XML_Char* name) 1269b7579f77SDag-Erling Smørgrav { 1270b7579f77SDag-Erling Smørgrav int i; 1271b7579f77SDag-Erling Smørgrav for(i=0; atts[i]; i+=2) { 1272b7579f77SDag-Erling Smørgrav if(strcasecmp(atts[i], name) == 0) 1273b7579f77SDag-Erling Smørgrav return atts[i+1]; 1274b7579f77SDag-Erling Smørgrav } 1275b7579f77SDag-Erling Smørgrav return NULL; 1276b7579f77SDag-Erling Smørgrav } 1277b7579f77SDag-Erling Smørgrav 1278b7579f77SDag-Erling Smørgrav /** 1279b7579f77SDag-Erling Smørgrav * XML convert DateTime element to time_t. 1280b7579f77SDag-Erling Smørgrav * [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] 1281b7579f77SDag-Erling Smørgrav * (with optional .ssssss fractional seconds) 1282b7579f77SDag-Erling Smørgrav * @param str: the string 1283b7579f77SDag-Erling Smørgrav * @return a time_t representation or 0 on failure. 1284b7579f77SDag-Erling Smørgrav */ 1285b7579f77SDag-Erling Smørgrav static time_t 1286b7579f77SDag-Erling Smørgrav xml_convertdate(const char* str) 1287b7579f77SDag-Erling Smørgrav { 1288b7579f77SDag-Erling Smørgrav time_t t = 0; 1289b7579f77SDag-Erling Smørgrav struct tm tm; 1290b7579f77SDag-Erling Smørgrav const char* s; 1291b7579f77SDag-Erling Smørgrav /* for this application, ignore minus in front; 1292b7579f77SDag-Erling Smørgrav * only positive dates are expected */ 1293b7579f77SDag-Erling Smørgrav s = str; 1294b7579f77SDag-Erling Smørgrav if(s[0] == '-') s++; 1295b7579f77SDag-Erling Smørgrav memset(&tm, 0, sizeof(tm)); 1296b7579f77SDag-Erling Smørgrav /* parse initial content of the string (lots of whitespace allowed) */ 1297b7579f77SDag-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); 1298b7579f77SDag-Erling Smørgrav if(!s) { 1299b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate parse failure %s\n", str); 1300b7579f77SDag-Erling Smørgrav return 0; 1301b7579f77SDag-Erling Smørgrav } 1302b7579f77SDag-Erling Smørgrav /* parse remainder of date string */ 1303b7579f77SDag-Erling Smørgrav if(*s == '.') { 1304b7579f77SDag-Erling Smørgrav /* optional '.' and fractional seconds */ 1305b7579f77SDag-Erling Smørgrav int frac = 0, n = 0; 1306b7579f77SDag-Erling Smørgrav if(sscanf(s+1, "%d%n", &frac, &n) < 1) { 1307b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate f failure %s\n", str); 1308b7579f77SDag-Erling Smørgrav return 0; 1309b7579f77SDag-Erling Smørgrav } 1310b7579f77SDag-Erling Smørgrav /* fraction is not used, time_t has second accuracy */ 1311b7579f77SDag-Erling Smørgrav s++; 1312b7579f77SDag-Erling Smørgrav s+=n; 1313b7579f77SDag-Erling Smørgrav } 1314b7579f77SDag-Erling Smørgrav if(*s == 'Z' || *s == 'z') { 1315b7579f77SDag-Erling Smørgrav /* nothing to do for this */ 1316b7579f77SDag-Erling Smørgrav s++; 1317b7579f77SDag-Erling Smørgrav } else if(*s == '+' || *s == '-') { 1318b7579f77SDag-Erling Smørgrav /* optional timezone spec: Z or +hh:mm or -hh:mm */ 1319b7579f77SDag-Erling Smørgrav int hr = 0, mn = 0, n = 0; 1320b7579f77SDag-Erling Smørgrav if(sscanf(s+1, "%d:%d%n", &hr, &mn, &n) < 2) { 1321b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate tz failure %s\n", str); 1322b7579f77SDag-Erling Smørgrav return 0; 1323b7579f77SDag-Erling Smørgrav } 1324b7579f77SDag-Erling Smørgrav if(*s == '+') { 1325b7579f77SDag-Erling Smørgrav tm.tm_hour += hr; 1326b7579f77SDag-Erling Smørgrav tm.tm_min += mn; 1327b7579f77SDag-Erling Smørgrav } else { 1328b7579f77SDag-Erling Smørgrav tm.tm_hour -= hr; 1329b7579f77SDag-Erling Smørgrav tm.tm_min -= mn; 1330b7579f77SDag-Erling Smørgrav } 1331b7579f77SDag-Erling Smørgrav s++; 1332b7579f77SDag-Erling Smørgrav s += n; 1333b7579f77SDag-Erling Smørgrav } 1334b7579f77SDag-Erling Smørgrav if(*s != 0) { 1335b7579f77SDag-Erling Smørgrav /* not ended properly */ 1336b7579f77SDag-Erling Smørgrav /* but ignore, (lenient) */ 1337b7579f77SDag-Erling Smørgrav } 1338b7579f77SDag-Erling Smørgrav 1339b75612f8SDag-Erling Smørgrav t = sldns_mktime_from_utc(&tm); 1340b7579f77SDag-Erling Smørgrav if(t == (time_t)-1) { 1341b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate mktime failure\n"); 1342b7579f77SDag-Erling Smørgrav return 0; 1343b7579f77SDag-Erling Smørgrav } 1344b7579f77SDag-Erling Smørgrav return t; 1345b7579f77SDag-Erling Smørgrav } 1346b7579f77SDag-Erling Smørgrav 1347b7579f77SDag-Erling Smørgrav /** 1348b7579f77SDag-Erling Smørgrav * XML handle the KeyDigest start tag, check validity periods. 1349b7579f77SDag-Erling Smørgrav */ 1350b7579f77SDag-Erling Smørgrav static void 1351b7579f77SDag-Erling Smørgrav handle_keydigest(struct xml_data* data, const XML_Char **atts) 1352b7579f77SDag-Erling Smørgrav { 1353b7579f77SDag-Erling Smørgrav data->use_key = 0; 1354b7579f77SDag-Erling Smørgrav if(find_att(atts, "validFrom")) { 1355b7579f77SDag-Erling Smørgrav time_t from = xml_convertdate(find_att(atts, "validFrom")); 1356b7579f77SDag-Erling Smørgrav if(from == 0) { 1357b7579f77SDag-Erling Smørgrav if(verb) printf("error: xml cannot be parsed\n"); 1358b7579f77SDag-Erling Smørgrav exit(0); 1359b7579f77SDag-Erling Smørgrav } 1360b7579f77SDag-Erling Smørgrav if(data->date < from) 1361b7579f77SDag-Erling Smørgrav return; 1362b7579f77SDag-Erling Smørgrav } 1363b7579f77SDag-Erling Smørgrav if(find_att(atts, "validUntil")) { 1364b7579f77SDag-Erling Smørgrav time_t until = xml_convertdate(find_att(atts, "validUntil")); 1365b7579f77SDag-Erling Smørgrav if(until == 0) { 1366b7579f77SDag-Erling Smørgrav if(verb) printf("error: xml cannot be parsed\n"); 1367b7579f77SDag-Erling Smørgrav exit(0); 1368b7579f77SDag-Erling Smørgrav } 1369b7579f77SDag-Erling Smørgrav if(data->date > until) 1370b7579f77SDag-Erling Smørgrav return; 1371b7579f77SDag-Erling Smørgrav } 1372b7579f77SDag-Erling Smørgrav /* yes we want to use this key */ 1373b7579f77SDag-Erling Smørgrav data->use_key = 1; 1374b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->ctag); 1375b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->calgo); 1376b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->cdigtype); 1377b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->cdigest); 1378b7579f77SDag-Erling Smørgrav } 1379b7579f77SDag-Erling Smørgrav 1380b7579f77SDag-Erling Smørgrav /** See if XML element equals the zone name */ 1381b7579f77SDag-Erling Smørgrav static int 1382ebc5657fSDag-Erling Smørgrav xml_is_zone_name(BIO* zone, const char* name) 1383b7579f77SDag-Erling Smørgrav { 1384b7579f77SDag-Erling Smørgrav char buf[1024]; 1385b7579f77SDag-Erling Smørgrav char* z = NULL; 1386b7579f77SDag-Erling Smørgrav long zlen; 1387b7579f77SDag-Erling Smørgrav (void)BIO_seek(zone, 0); 1388b7579f77SDag-Erling Smørgrav zlen = BIO_get_mem_data(zone, &z); 1389b7579f77SDag-Erling Smørgrav if(!zlen || !z) return 0; 1390b7579f77SDag-Erling Smørgrav /* zero terminate */ 1391b7579f77SDag-Erling Smørgrav if(zlen >= (long)sizeof(buf)) return 0; 1392b7579f77SDag-Erling Smørgrav memmove(buf, z, (size_t)zlen); 1393b7579f77SDag-Erling Smørgrav buf[zlen] = 0; 1394b7579f77SDag-Erling Smørgrav /* compare */ 1395b7579f77SDag-Erling Smørgrav return (strncasecmp(buf, name, strlen(name)) == 0); 1396b7579f77SDag-Erling Smørgrav } 1397b7579f77SDag-Erling Smørgrav 1398b7579f77SDag-Erling Smørgrav /** 1399b7579f77SDag-Erling Smørgrav * XML start of element. This callback is called whenever an XML tag starts. 1400b7579f77SDag-Erling Smørgrav * XML_Char is UTF8. 1401b7579f77SDag-Erling Smørgrav * @param userData: the xml_data structure. 1402b7579f77SDag-Erling Smørgrav * @param name: the tag that starts. 1403b7579f77SDag-Erling Smørgrav * @param atts: array of strings, pairs of attr = value, ends with NULL. 1404b7579f77SDag-Erling Smørgrav * i.e. att[0]="att[1]" att[2]="att[3]" att[4]isNull 1405b7579f77SDag-Erling Smørgrav */ 1406b7579f77SDag-Erling Smørgrav static void 1407b7579f77SDag-Erling Smørgrav xml_startelem(void *userData, const XML_Char *name, const XML_Char **atts) 1408b7579f77SDag-Erling Smørgrav { 1409b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1410b7579f77SDag-Erling Smørgrav BIO* b; 1411b7579f77SDag-Erling Smørgrav if(verb>=4) printf("xml tag start '%s'\n", name); 1412b7579f77SDag-Erling Smørgrav free(data->tag); 1413b7579f77SDag-Erling Smørgrav data->tag = strdup(name); 1414b7579f77SDag-Erling Smørgrav if(!data->tag) { 1415b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1416b7579f77SDag-Erling Smørgrav exit(0); 1417b7579f77SDag-Erling Smørgrav } 1418b7579f77SDag-Erling Smørgrav if(verb>=4) { 1419b7579f77SDag-Erling Smørgrav int i; 1420b7579f77SDag-Erling Smørgrav for(i=0; atts[i]; i+=2) { 1421b7579f77SDag-Erling Smørgrav printf(" %s='%s'\n", atts[i], atts[i+1]); 1422b7579f77SDag-Erling Smørgrav } 1423b7579f77SDag-Erling Smørgrav } 1424b7579f77SDag-Erling Smørgrav /* handle attributes to particular types */ 1425b7579f77SDag-Erling Smørgrav if(strcasecmp(name, "KeyDigest") == 0) { 1426b7579f77SDag-Erling Smørgrav handle_keydigest(data, atts); 1427b7579f77SDag-Erling Smørgrav return; 1428b7579f77SDag-Erling Smørgrav } else if(strcasecmp(name, "Zone") == 0) { 1429b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->czone); 1430b7579f77SDag-Erling Smørgrav return; 1431b7579f77SDag-Erling Smørgrav } 1432b7579f77SDag-Erling Smørgrav 1433b7579f77SDag-Erling Smørgrav /* for other types we prepare to pick up the data */ 1434b7579f77SDag-Erling Smørgrav if(!data->use_key) 1435b7579f77SDag-Erling Smørgrav return; 1436b7579f77SDag-Erling Smørgrav b = xml_selectbio(data, data->tag); 1437b7579f77SDag-Erling Smørgrav if(b) { 1438b7579f77SDag-Erling Smørgrav /* empty it */ 1439b7579f77SDag-Erling Smørgrav (void)BIO_reset(b); 1440b7579f77SDag-Erling Smørgrav } 1441b7579f77SDag-Erling Smørgrav } 1442b7579f77SDag-Erling Smørgrav 1443b7579f77SDag-Erling Smørgrav /** Append str to bio */ 1444b7579f77SDag-Erling Smørgrav static void 1445b7579f77SDag-Erling Smørgrav xml_append_str(BIO* b, const char* s) 1446b7579f77SDag-Erling Smørgrav { 144717d15b25SDag-Erling Smørgrav if(BIO_write(b, s, (int)strlen(s)) < 0) { 1448b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1449b7579f77SDag-Erling Smørgrav exit(0); 1450b7579f77SDag-Erling Smørgrav } 1451b7579f77SDag-Erling Smørgrav } 1452b7579f77SDag-Erling Smørgrav 1453b7579f77SDag-Erling Smørgrav /** Append bio to bio */ 1454b7579f77SDag-Erling Smørgrav static void 1455b7579f77SDag-Erling Smørgrav xml_append_bio(BIO* b, BIO* a) 1456b7579f77SDag-Erling Smørgrav { 1457b7579f77SDag-Erling Smørgrav char* z = NULL; 1458b7579f77SDag-Erling Smørgrav long i, len; 1459b7579f77SDag-Erling Smørgrav (void)BIO_seek(a, 0); 1460b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(a, &z); 1461b7579f77SDag-Erling Smørgrav if(!len || !z) { 1462b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1463b7579f77SDag-Erling Smørgrav exit(0); 1464b7579f77SDag-Erling Smørgrav } 1465b7579f77SDag-Erling Smørgrav /* remove newlines in the data here */ 1466b7579f77SDag-Erling Smørgrav for(i=0; i<len; i++) { 1467b7579f77SDag-Erling Smørgrav if(z[i] == '\r' || z[i] == '\n') 1468b7579f77SDag-Erling Smørgrav z[i] = ' '; 1469b7579f77SDag-Erling Smørgrav } 1470b7579f77SDag-Erling Smørgrav /* write to BIO */ 147117d15b25SDag-Erling Smørgrav if(BIO_write(b, z, len) < 0) { 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 } 1476b7579f77SDag-Erling Smørgrav 1477b7579f77SDag-Erling Smørgrav /** write the parsed xml-DS to the DS list */ 1478b7579f77SDag-Erling Smørgrav static void 1479b7579f77SDag-Erling Smørgrav xml_append_ds(struct xml_data* data) 1480b7579f77SDag-Erling Smørgrav { 1481b7579f77SDag-Erling Smørgrav /* write DS to accumulated DS */ 1482b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, ". IN DS "); 1483b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->ctag); 1484b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1485b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->calgo); 1486b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1487b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->cdigtype); 1488b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1489b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->cdigest); 1490b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, "\n"); 1491b7579f77SDag-Erling Smørgrav data->num_keys++; 1492b7579f77SDag-Erling Smørgrav } 1493b7579f77SDag-Erling Smørgrav 1494b7579f77SDag-Erling Smørgrav /** 1495b7579f77SDag-Erling Smørgrav * XML end of element. This callback is called whenever an XML tag ends. 1496b7579f77SDag-Erling Smørgrav * XML_Char is UTF8. 1497b7579f77SDag-Erling Smørgrav * @param userData: the xml_data structure 1498b7579f77SDag-Erling Smørgrav * @param name: the tag that ends. 1499b7579f77SDag-Erling Smørgrav */ 1500b7579f77SDag-Erling Smørgrav static void 1501b7579f77SDag-Erling Smørgrav xml_endelem(void *userData, const XML_Char *name) 1502b7579f77SDag-Erling Smørgrav { 1503b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1504b7579f77SDag-Erling Smørgrav if(verb>=4) printf("xml tag end '%s'\n", name); 1505b7579f77SDag-Erling Smørgrav free(data->tag); 1506b7579f77SDag-Erling Smørgrav data->tag = NULL; 1507b7579f77SDag-Erling Smørgrav if(strcasecmp(name, "KeyDigest") == 0) { 1508b7579f77SDag-Erling Smørgrav if(data->use_key) 1509b7579f77SDag-Erling Smørgrav xml_append_ds(data); 1510b7579f77SDag-Erling Smørgrav data->use_key = 0; 1511b7579f77SDag-Erling Smørgrav } else if(strcasecmp(name, "Zone") == 0) { 1512b7579f77SDag-Erling Smørgrav if(!xml_is_zone_name(data->czone, ".")) { 1513b7579f77SDag-Erling Smørgrav if(verb) printf("xml not for the right zone\n"); 1514b7579f77SDag-Erling Smørgrav exit(0); 1515b7579f77SDag-Erling Smørgrav } 1516b7579f77SDag-Erling Smørgrav } 1517b7579f77SDag-Erling Smørgrav } 1518b7579f77SDag-Erling Smørgrav 15198ed2b524SDag-Erling Smørgrav /* Stop the parser when an entity declaration is encountered. For safety. */ 15208ed2b524SDag-Erling Smørgrav static void 15218ed2b524SDag-Erling Smørgrav xml_entitydeclhandler(void *userData, 15228ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(entityName), 15238ed2b524SDag-Erling Smørgrav int ATTR_UNUSED(is_parameter_entity), 15248ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(value), int ATTR_UNUSED(value_length), 15258ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(base), 15268ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(systemId), 15278ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(publicId), 15288ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(notationName)) 15298ed2b524SDag-Erling Smørgrav { 153005ab2901SDag-Erling Smørgrav #if HAVE_DECL_XML_STOPPARSER 15318ed2b524SDag-Erling Smørgrav (void)XML_StopParser((XML_Parser)userData, XML_FALSE); 153205ab2901SDag-Erling Smørgrav #else 153305ab2901SDag-Erling Smørgrav (void)userData; 153405ab2901SDag-Erling Smørgrav #endif 15358ed2b524SDag-Erling Smørgrav } 15368ed2b524SDag-Erling Smørgrav 1537b7579f77SDag-Erling Smørgrav /** 1538b7579f77SDag-Erling Smørgrav * XML parser setup of the callbacks for the tags 1539b7579f77SDag-Erling Smørgrav */ 1540b7579f77SDag-Erling Smørgrav static void 1541b7579f77SDag-Erling Smørgrav xml_parse_setup(XML_Parser parser, struct xml_data* data, time_t now) 1542b7579f77SDag-Erling Smørgrav { 1543b7579f77SDag-Erling Smørgrav char buf[1024]; 1544b7579f77SDag-Erling Smørgrav memset(data, 0, sizeof(*data)); 1545b7579f77SDag-Erling Smørgrav XML_SetUserData(parser, data); 1546b7579f77SDag-Erling Smørgrav data->parser = parser; 1547b7579f77SDag-Erling Smørgrav data->date = now; 1548b7579f77SDag-Erling Smørgrav data->ds = BIO_new(BIO_s_mem()); 1549b7579f77SDag-Erling Smørgrav data->ctag = BIO_new(BIO_s_mem()); 1550b7579f77SDag-Erling Smørgrav data->czone = BIO_new(BIO_s_mem()); 1551b7579f77SDag-Erling Smørgrav data->calgo = BIO_new(BIO_s_mem()); 1552b7579f77SDag-Erling Smørgrav data->cdigtype = BIO_new(BIO_s_mem()); 1553b7579f77SDag-Erling Smørgrav data->cdigest = BIO_new(BIO_s_mem()); 1554b7579f77SDag-Erling Smørgrav if(!data->ds || !data->ctag || !data->calgo || !data->czone || 1555b7579f77SDag-Erling Smørgrav !data->cdigtype || !data->cdigest) { 1556b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1557b7579f77SDag-Erling Smørgrav exit(0); 1558b7579f77SDag-Erling Smørgrav } 1559b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), "; created by unbound-anchor on %s", 1560b7579f77SDag-Erling Smørgrav ctime(&now)); 156117d15b25SDag-Erling Smørgrav if(BIO_write(data->ds, buf, (int)strlen(buf)) < 0) { 1562b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1563b7579f77SDag-Erling Smørgrav exit(0); 1564b7579f77SDag-Erling Smørgrav } 15658ed2b524SDag-Erling Smørgrav XML_SetEntityDeclHandler(parser, xml_entitydeclhandler); 1566b7579f77SDag-Erling Smørgrav XML_SetElementHandler(parser, xml_startelem, xml_endelem); 1567b7579f77SDag-Erling Smørgrav XML_SetCharacterDataHandler(parser, xml_charhandle); 1568b7579f77SDag-Erling Smørgrav } 1569b7579f77SDag-Erling Smørgrav 1570b7579f77SDag-Erling Smørgrav /** 1571b7579f77SDag-Erling Smørgrav * Perform XML parsing of the root-anchors file 1572b7579f77SDag-Erling Smørgrav * Its format description can be read here 1573b7579f77SDag-Erling Smørgrav * https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt 1574b7579f77SDag-Erling Smørgrav * It uses libexpat. 1575b7579f77SDag-Erling Smørgrav * @param xml: BIO with xml data. 1576b7579f77SDag-Erling Smørgrav * @param now: the current time for checking DS validity periods. 1577b7579f77SDag-Erling Smørgrav * @return memoryBIO with the DS data in zone format. 1578b7579f77SDag-Erling Smørgrav * or NULL if the zone is insecure. 1579b7579f77SDag-Erling Smørgrav * (It exit()s on error) 1580b7579f77SDag-Erling Smørgrav */ 1581b7579f77SDag-Erling Smørgrav static BIO* 1582b7579f77SDag-Erling Smørgrav xml_parse(BIO* xml, time_t now) 1583b7579f77SDag-Erling Smørgrav { 1584b7579f77SDag-Erling Smørgrav char* pp; 1585b7579f77SDag-Erling Smørgrav int len; 1586b7579f77SDag-Erling Smørgrav XML_Parser parser; 1587b7579f77SDag-Erling Smørgrav struct xml_data data; 1588b7579f77SDag-Erling Smørgrav 1589b7579f77SDag-Erling Smørgrav parser = XML_ParserCreate(NULL); 1590b7579f77SDag-Erling Smørgrav if(!parser) { 1591b7579f77SDag-Erling Smørgrav if(verb) printf("could not XML_ParserCreate\n"); 1592b7579f77SDag-Erling Smørgrav exit(0); 1593b7579f77SDag-Erling Smørgrav } 1594b7579f77SDag-Erling Smørgrav 1595b7579f77SDag-Erling Smørgrav /* setup callbacks */ 1596b7579f77SDag-Erling Smørgrav xml_parse_setup(parser, &data, now); 1597b7579f77SDag-Erling Smørgrav 1598b7579f77SDag-Erling Smørgrav /* parse it */ 1599b7579f77SDag-Erling Smørgrav (void)BIO_reset(xml); 1600b7579f77SDag-Erling Smørgrav len = (int)BIO_get_mem_data(xml, &pp); 1601b7579f77SDag-Erling Smørgrav if(!len || !pp) { 1602b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1603b7579f77SDag-Erling Smørgrav exit(0); 1604b7579f77SDag-Erling Smørgrav } 1605b7579f77SDag-Erling Smørgrav if(!XML_Parse(parser, pp, len, 1 /*isfinal*/ )) { 1606b7579f77SDag-Erling Smørgrav const char *e = XML_ErrorString(XML_GetErrorCode(parser)); 1607b7579f77SDag-Erling Smørgrav if(verb) printf("XML_Parse failure %s\n", e?e:""); 1608b7579f77SDag-Erling Smørgrav exit(0); 1609b7579f77SDag-Erling Smørgrav } 1610b7579f77SDag-Erling Smørgrav 1611b7579f77SDag-Erling Smørgrav /* parsed */ 1612b7579f77SDag-Erling Smørgrav if(verb) printf("XML was parsed successfully, %d keys\n", 1613b7579f77SDag-Erling Smørgrav data.num_keys); 1614b7579f77SDag-Erling Smørgrav free(data.tag); 1615b7579f77SDag-Erling Smørgrav XML_ParserFree(parser); 1616b7579f77SDag-Erling Smørgrav 1617b7579f77SDag-Erling Smørgrav if(verb >= 4) { 1618b7579f77SDag-Erling Smørgrav (void)BIO_seek(data.ds, 0); 1619b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(data.ds, &pp); 1620b7579f77SDag-Erling Smørgrav printf("got DS bio %d: '", len); 1621b7579f77SDag-Erling Smørgrav if(!fwrite(pp, (size_t)len, 1, stdout)) 1622b7579f77SDag-Erling Smørgrav /* compilers do not allow us to ignore fwrite .. */ 1623b7579f77SDag-Erling Smørgrav fprintf(stderr, "error writing to stdout\n"); 1624b7579f77SDag-Erling Smørgrav printf("'\n"); 1625b7579f77SDag-Erling Smørgrav } 1626b7579f77SDag-Erling Smørgrav BIO_free(data.czone); 1627b7579f77SDag-Erling Smørgrav BIO_free(data.ctag); 1628b7579f77SDag-Erling Smørgrav BIO_free(data.calgo); 1629b7579f77SDag-Erling Smørgrav BIO_free(data.cdigtype); 1630b7579f77SDag-Erling Smørgrav BIO_free(data.cdigest); 1631b7579f77SDag-Erling Smørgrav 1632b7579f77SDag-Erling Smørgrav if(data.num_keys == 0) { 1633b7579f77SDag-Erling Smørgrav /* the root zone seems to have gone insecure */ 1634b7579f77SDag-Erling Smørgrav BIO_free(data.ds); 1635b7579f77SDag-Erling Smørgrav return NULL; 1636b7579f77SDag-Erling Smørgrav } else { 1637b7579f77SDag-Erling Smørgrav return data.ds; 1638b7579f77SDag-Erling Smørgrav } 1639b7579f77SDag-Erling Smørgrav } 1640b7579f77SDag-Erling Smørgrav 16418ed2b524SDag-Erling Smørgrav /* get key usage out of its extension, returns 0 if no key_usage extension */ 16428ed2b524SDag-Erling Smørgrav static unsigned long 16438ed2b524SDag-Erling Smørgrav get_usage_of_ex(X509* cert) 16448ed2b524SDag-Erling Smørgrav { 16458ed2b524SDag-Erling Smørgrav unsigned long val = 0; 16468ed2b524SDag-Erling Smørgrav ASN1_BIT_STRING* s; 16478ed2b524SDag-Erling Smørgrav if((s=X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL))) { 16488ed2b524SDag-Erling Smørgrav if(s->length > 0) { 16498ed2b524SDag-Erling Smørgrav val = s->data[0]; 16508ed2b524SDag-Erling Smørgrav if(s->length > 1) 16518ed2b524SDag-Erling Smørgrav val |= s->data[1] << 8; 16528ed2b524SDag-Erling Smørgrav } 16538ed2b524SDag-Erling Smørgrav ASN1_BIT_STRING_free(s); 16548ed2b524SDag-Erling Smørgrav } 16558ed2b524SDag-Erling Smørgrav return val; 16568ed2b524SDag-Erling Smørgrav } 16578ed2b524SDag-Erling Smørgrav 16588ed2b524SDag-Erling Smørgrav /** get valid signers from the list of signers in the signature */ 16598ed2b524SDag-Erling Smørgrav static STACK_OF(X509)* 1660ebc5657fSDag-Erling Smørgrav get_valid_signers(PKCS7* p7, const char* p7signer) 16618ed2b524SDag-Erling Smørgrav { 16628ed2b524SDag-Erling Smørgrav int i; 16638ed2b524SDag-Erling Smørgrav STACK_OF(X509)* validsigners = sk_X509_new_null(); 16648ed2b524SDag-Erling Smørgrav STACK_OF(X509)* signers = PKCS7_get0_signers(p7, NULL, 0); 16658ed2b524SDag-Erling Smørgrav unsigned long usage = 0; 16668ed2b524SDag-Erling Smørgrav if(!validsigners) { 16678ed2b524SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 16688ed2b524SDag-Erling Smørgrav sk_X509_free(signers); 16698ed2b524SDag-Erling Smørgrav return NULL; 16708ed2b524SDag-Erling Smørgrav } 16718ed2b524SDag-Erling Smørgrav if(!signers) { 16728ed2b524SDag-Erling Smørgrav if(verb) printf("no signers in pkcs7 signature\n"); 16738ed2b524SDag-Erling Smørgrav sk_X509_free(validsigners); 16748ed2b524SDag-Erling Smørgrav return NULL; 16758ed2b524SDag-Erling Smørgrav } 16768ed2b524SDag-Erling Smørgrav for(i=0; i<sk_X509_num(signers); i++) { 16778ed2b524SDag-Erling Smørgrav X509_NAME* nm = X509_get_subject_name( 16788ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i)); 16798ed2b524SDag-Erling Smørgrav char buf[1024]; 16808ed2b524SDag-Erling Smørgrav if(!nm) { 16818ed2b524SDag-Erling Smørgrav if(verb) printf("signer %d: cert has no subject name\n", i); 16828ed2b524SDag-Erling Smørgrav continue; 16838ed2b524SDag-Erling Smørgrav } 16848ed2b524SDag-Erling Smørgrav if(verb && nm) { 16858ed2b524SDag-Erling Smørgrav char* nmline = X509_NAME_oneline(nm, buf, 16868ed2b524SDag-Erling Smørgrav (int)sizeof(buf)); 16878ed2b524SDag-Erling Smørgrav printf("signer %d: Subject: %s\n", i, 16888ed2b524SDag-Erling Smørgrav nmline?nmline:"no subject"); 16898ed2b524SDag-Erling Smørgrav if(verb >= 3 && X509_NAME_get_text_by_NID(nm, 16908ed2b524SDag-Erling Smørgrav NID_commonName, buf, (int)sizeof(buf))) 16918ed2b524SDag-Erling Smørgrav printf("commonName: %s\n", buf); 16928ed2b524SDag-Erling Smørgrav if(verb >= 3 && X509_NAME_get_text_by_NID(nm, 16938ed2b524SDag-Erling Smørgrav NID_pkcs9_emailAddress, buf, (int)sizeof(buf))) 16948ed2b524SDag-Erling Smørgrav printf("emailAddress: %s\n", buf); 16958ed2b524SDag-Erling Smørgrav } 16968ed2b524SDag-Erling Smørgrav if(verb) { 16978ed2b524SDag-Erling Smørgrav int ku_loc = X509_get_ext_by_NID( 16988ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i), NID_key_usage, -1); 16998ed2b524SDag-Erling Smørgrav if(verb >= 3 && ku_loc >= 0) { 17008ed2b524SDag-Erling Smørgrav X509_EXTENSION *ex = X509_get_ext( 17018ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i), ku_loc); 17028ed2b524SDag-Erling Smørgrav if(ex) { 17038ed2b524SDag-Erling Smørgrav printf("keyUsage: "); 17048ed2b524SDag-Erling Smørgrav X509V3_EXT_print_fp(stdout, ex, 0, 0); 17058ed2b524SDag-Erling Smørgrav printf("\n"); 17068ed2b524SDag-Erling Smørgrav } 17078ed2b524SDag-Erling Smørgrav } 17088ed2b524SDag-Erling Smørgrav } 17098ed2b524SDag-Erling Smørgrav if(!p7signer || strcmp(p7signer, "")==0) { 17108ed2b524SDag-Erling Smørgrav /* there is no name to check, return all records */ 17118ed2b524SDag-Erling Smørgrav if(verb) printf("did not check commonName of signer\n"); 17128ed2b524SDag-Erling Smørgrav } else { 17138ed2b524SDag-Erling Smørgrav if(!X509_NAME_get_text_by_NID(nm, 17148ed2b524SDag-Erling Smørgrav NID_pkcs9_emailAddress, 17158ed2b524SDag-Erling Smørgrav buf, (int)sizeof(buf))) { 17168ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with no name\n"); 17178ed2b524SDag-Erling Smørgrav continue; /* no name, no use */ 17188ed2b524SDag-Erling Smørgrav } 17198ed2b524SDag-Erling Smørgrav if(strcmp(buf, p7signer) != 0) { 17208ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with wrong name\n"); 17218ed2b524SDag-Erling Smørgrav continue; /* wrong name, skip it */ 17228ed2b524SDag-Erling Smørgrav } 17238ed2b524SDag-Erling Smørgrav } 17248ed2b524SDag-Erling Smørgrav 17258ed2b524SDag-Erling Smørgrav /* check that the key usage allows digital signatures 17268ed2b524SDag-Erling Smørgrav * (the p7s) */ 17278ed2b524SDag-Erling Smørgrav usage = get_usage_of_ex(sk_X509_value(signers, i)); 17288ed2b524SDag-Erling Smørgrav if(!(usage & KU_DIGITAL_SIGNATURE)) { 17298ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with no key usage Digital Signature allowed\n"); 17308ed2b524SDag-Erling Smørgrav continue; 17318ed2b524SDag-Erling Smørgrav } 17328ed2b524SDag-Erling Smørgrav 17338ed2b524SDag-Erling Smørgrav /* we like this cert, add it to our list of valid 17348ed2b524SDag-Erling Smørgrav * signers certificates */ 17358ed2b524SDag-Erling Smørgrav sk_X509_push(validsigners, sk_X509_value(signers, i)); 17368ed2b524SDag-Erling Smørgrav } 17378ed2b524SDag-Erling Smørgrav sk_X509_free(signers); 17388ed2b524SDag-Erling Smørgrav return validsigners; 17398ed2b524SDag-Erling Smørgrav } 17408ed2b524SDag-Erling Smørgrav 1741b7579f77SDag-Erling Smørgrav /** verify a PKCS7 signature, false on failure */ 1742b7579f77SDag-Erling Smørgrav static int 1743ebc5657fSDag-Erling Smørgrav verify_p7sig(BIO* data, BIO* p7s, STACK_OF(X509)* trust, const char* p7signer) 1744b7579f77SDag-Erling Smørgrav { 1745b7579f77SDag-Erling Smørgrav PKCS7* p7; 1746b7579f77SDag-Erling Smørgrav X509_STORE *store = X509_STORE_new(); 17478ed2b524SDag-Erling Smørgrav STACK_OF(X509)* validsigners; 1748b7579f77SDag-Erling Smørgrav int secure = 0; 1749b7579f77SDag-Erling Smørgrav int i; 1750b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 1751b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); 1752b7579f77SDag-Erling Smørgrav if(!param) { 1753b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1754b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1755b7579f77SDag-Erling Smørgrav return 0; 1756b7579f77SDag-Erling Smørgrav } 1757b7579f77SDag-Erling Smørgrav /* do the selfcheck on the root certificate; it checks that the 1758b7579f77SDag-Erling Smørgrav * input is valid */ 1759b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CHECK_SS_SIGNATURE); 1760b7579f77SDag-Erling Smørgrav if(store) X509_STORE_set1_param(store, param); 1761b7579f77SDag-Erling Smørgrav #endif 1762b7579f77SDag-Erling Smørgrav if(!store) { 1763b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1764b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 1765b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM_free(param); 1766b7579f77SDag-Erling Smørgrav #endif 1767b7579f77SDag-Erling Smørgrav return 0; 1768b7579f77SDag-Erling Smørgrav } 17698ed2b524SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 17708ed2b524SDag-Erling Smørgrav X509_VERIFY_PARAM_free(param); 17718ed2b524SDag-Erling Smørgrav #endif 1772b7579f77SDag-Erling Smørgrav 1773b7579f77SDag-Erling Smørgrav (void)BIO_reset(p7s); 1774b7579f77SDag-Erling Smørgrav (void)BIO_reset(data); 1775b7579f77SDag-Erling Smørgrav 1776b7579f77SDag-Erling Smørgrav /* convert p7s to p7 (the signature) */ 1777b7579f77SDag-Erling Smørgrav p7 = d2i_PKCS7_bio(p7s, NULL); 1778b7579f77SDag-Erling Smørgrav if(!p7) { 1779b7579f77SDag-Erling Smørgrav if(verb) printf("could not parse p7s signature file\n"); 1780b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1781b7579f77SDag-Erling Smørgrav return 0; 1782b7579f77SDag-Erling Smørgrav } 1783b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("parsed the PKCS7 signature\n"); 1784b7579f77SDag-Erling Smørgrav 1785b7579f77SDag-Erling Smørgrav /* convert trust to trusted certificate store */ 1786b7579f77SDag-Erling Smørgrav for(i=0; i<sk_X509_num(trust); i++) { 1787b7579f77SDag-Erling Smørgrav if(!X509_STORE_add_cert(store, sk_X509_value(trust, i))) { 1788b7579f77SDag-Erling Smørgrav if(verb) printf("failed X509_STORE_add_cert\n"); 1789b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1790b7579f77SDag-Erling Smørgrav PKCS7_free(p7); 1791b7579f77SDag-Erling Smørgrav return 0; 1792b7579f77SDag-Erling Smørgrav } 1793b7579f77SDag-Erling Smørgrav } 1794b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("setup the X509_STORE\n"); 1795b7579f77SDag-Erling Smørgrav 17968ed2b524SDag-Erling Smørgrav /* check what is in the Subject name of the certificates, 17978ed2b524SDag-Erling Smørgrav * and build a stack that contains only the right certificates */ 17988ed2b524SDag-Erling Smørgrav validsigners = get_valid_signers(p7, p7signer); 17998ed2b524SDag-Erling Smørgrav if(!validsigners) { 18008ed2b524SDag-Erling Smørgrav X509_STORE_free(store); 18018ed2b524SDag-Erling Smørgrav PKCS7_free(p7); 18028ed2b524SDag-Erling Smørgrav return 0; 18038ed2b524SDag-Erling Smørgrav } 18048ed2b524SDag-Erling Smørgrav if(PKCS7_verify(p7, validsigners, store, data, NULL, PKCS7_NOINTERN) == 1) { 1805b7579f77SDag-Erling Smørgrav secure = 1; 1806b7579f77SDag-Erling Smørgrav if(verb) printf("the PKCS7 signature verified\n"); 1807b7579f77SDag-Erling Smørgrav } else { 1808b7579f77SDag-Erling Smørgrav if(verb) { 1809b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 1810b7579f77SDag-Erling Smørgrav } 1811b7579f77SDag-Erling Smørgrav } 1812b7579f77SDag-Erling Smørgrav 18138ed2b524SDag-Erling Smørgrav sk_X509_free(validsigners); 1814b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1815b7579f77SDag-Erling Smørgrav PKCS7_free(p7); 1816b7579f77SDag-Erling Smørgrav return secure; 1817b7579f77SDag-Erling Smørgrav } 1818b7579f77SDag-Erling Smørgrav 1819b7579f77SDag-Erling Smørgrav /** write unsigned root anchor file, a 5011 revoked tp */ 1820b7579f77SDag-Erling Smørgrav static void 1821ebc5657fSDag-Erling Smørgrav write_unsigned_root(const char* root_anchor_file) 1822b7579f77SDag-Erling Smørgrav { 1823b7579f77SDag-Erling Smørgrav FILE* out; 1824b7579f77SDag-Erling Smørgrav time_t now = time(NULL); 1825b7579f77SDag-Erling Smørgrav out = fopen(root_anchor_file, "w"); 1826b7579f77SDag-Erling Smørgrav if(!out) { 1827b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); 1828b7579f77SDag-Erling Smørgrav return; 1829b7579f77SDag-Erling Smørgrav } 1830b7579f77SDag-Erling Smørgrav if(fprintf(out, "; autotrust trust anchor file\n" 1831b7579f77SDag-Erling Smørgrav ";;REVOKED\n" 1832b7579f77SDag-Erling Smørgrav ";;id: . 1\n" 1833b7579f77SDag-Erling Smørgrav "; This file was written by unbound-anchor on %s" 1834b7579f77SDag-Erling Smørgrav "; It indicates that the root does not use DNSSEC\n" 1835b7579f77SDag-Erling Smørgrav "; to restart DNSSEC overwrite this file with a\n" 1836b7579f77SDag-Erling Smørgrav "; valid trustanchor or (empty-it and run unbound-anchor)\n" 1837b7579f77SDag-Erling Smørgrav , ctime(&now)) < 0) { 1838b7579f77SDag-Erling Smørgrav if(verb) printf("failed to write 'unsigned' to %s\n", 1839b7579f77SDag-Erling Smørgrav root_anchor_file); 1840b7579f77SDag-Erling Smørgrav if(verb && errno != 0) printf("%s\n", strerror(errno)); 1841b7579f77SDag-Erling Smørgrav } 184205ab2901SDag-Erling Smørgrav fflush(out); 184305ab2901SDag-Erling Smørgrav #ifdef HAVE_FSYNC 184405ab2901SDag-Erling Smørgrav fsync(fileno(out)); 184505ab2901SDag-Erling Smørgrav #else 1846b5663de9SDag-Erling Smørgrav FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); 184705ab2901SDag-Erling Smørgrav #endif 1848b7579f77SDag-Erling Smørgrav fclose(out); 1849b7579f77SDag-Erling Smørgrav } 1850b7579f77SDag-Erling Smørgrav 1851b7579f77SDag-Erling Smørgrav /** write root anchor file */ 1852b7579f77SDag-Erling Smørgrav static void 1853ebc5657fSDag-Erling Smørgrav write_root_anchor(const char* root_anchor_file, BIO* ds) 1854b7579f77SDag-Erling Smørgrav { 1855b7579f77SDag-Erling Smørgrav char* pp = NULL; 1856b7579f77SDag-Erling Smørgrav int len; 1857b7579f77SDag-Erling Smørgrav FILE* out; 1858b7579f77SDag-Erling Smørgrav (void)BIO_seek(ds, 0); 1859b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(ds, &pp); 1860b7579f77SDag-Erling Smørgrav if(!len || !pp) { 1861b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1862b7579f77SDag-Erling Smørgrav return; 1863b7579f77SDag-Erling Smørgrav } 1864b7579f77SDag-Erling Smørgrav out = fopen(root_anchor_file, "w"); 1865b7579f77SDag-Erling Smørgrav if(!out) { 1866b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); 1867b7579f77SDag-Erling Smørgrav return; 1868b7579f77SDag-Erling Smørgrav } 1869b7579f77SDag-Erling Smørgrav if(fwrite(pp, (size_t)len, 1, out) != 1) { 1870b7579f77SDag-Erling Smørgrav if(verb) printf("failed to write all data to %s\n", 1871b7579f77SDag-Erling Smørgrav root_anchor_file); 1872b7579f77SDag-Erling Smørgrav if(verb && errno != 0) printf("%s\n", strerror(errno)); 1873b7579f77SDag-Erling Smørgrav } 187405ab2901SDag-Erling Smørgrav fflush(out); 187505ab2901SDag-Erling Smørgrav #ifdef HAVE_FSYNC 187605ab2901SDag-Erling Smørgrav fsync(fileno(out)); 187705ab2901SDag-Erling Smørgrav #else 1878b5663de9SDag-Erling Smørgrav FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); 187905ab2901SDag-Erling Smørgrav #endif 1880b7579f77SDag-Erling Smørgrav fclose(out); 1881b7579f77SDag-Erling Smørgrav } 1882b7579f77SDag-Erling Smørgrav 1883b7579f77SDag-Erling Smørgrav /** Perform the verification and update of the trustanchor file */ 1884b7579f77SDag-Erling Smørgrav static void 1885ebc5657fSDag-Erling Smørgrav verify_and_update_anchor(const char* root_anchor_file, BIO* xml, BIO* p7s, 1886ebc5657fSDag-Erling Smørgrav STACK_OF(X509)* cert, const char* p7signer) 1887b7579f77SDag-Erling Smørgrav { 1888b7579f77SDag-Erling Smørgrav BIO* ds; 1889b7579f77SDag-Erling Smørgrav 1890b7579f77SDag-Erling Smørgrav /* verify xml file */ 18918ed2b524SDag-Erling Smørgrav if(!verify_p7sig(xml, p7s, cert, p7signer)) { 1892b7579f77SDag-Erling Smørgrav printf("the PKCS7 signature failed\n"); 1893b7579f77SDag-Erling Smørgrav exit(0); 1894b7579f77SDag-Erling Smørgrav } 1895b7579f77SDag-Erling Smørgrav 1896b7579f77SDag-Erling Smørgrav /* parse the xml file into DS records */ 1897b7579f77SDag-Erling Smørgrav ds = xml_parse(xml, time(NULL)); 1898b7579f77SDag-Erling Smørgrav if(!ds) { 1899b7579f77SDag-Erling Smørgrav /* the root zone is unsigned now */ 1900b7579f77SDag-Erling Smørgrav write_unsigned_root(root_anchor_file); 1901b7579f77SDag-Erling Smørgrav } else { 1902b7579f77SDag-Erling Smørgrav /* reinstate 5011 tracking */ 1903b7579f77SDag-Erling Smørgrav write_root_anchor(root_anchor_file, ds); 1904b7579f77SDag-Erling Smørgrav } 1905b7579f77SDag-Erling Smørgrav BIO_free(ds); 1906b7579f77SDag-Erling Smørgrav } 1907b7579f77SDag-Erling Smørgrav 1908b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK 1909b7579f77SDag-Erling Smørgrav static void do_wsa_cleanup(void) { WSACleanup(); } 1910b7579f77SDag-Erling Smørgrav #endif 1911b7579f77SDag-Erling Smørgrav 1912b7579f77SDag-Erling Smørgrav /** perform actual certupdate work */ 1913b7579f77SDag-Erling Smørgrav static int 1914ebc5657fSDag-Erling Smørgrav do_certupdate(const char* root_anchor_file, const char* root_cert_file, 1915ebc5657fSDag-Erling Smørgrav const char* urlname, const char* xmlname, const char* p7sname, 1916ebc5657fSDag-Erling Smørgrav const char* p7signer, const char* res_conf, const char* root_hints, 1917*4c75e3aaSDag-Erling Smørgrav const char* debugconf, int ip4only, int ip6only, int port) 1918b7579f77SDag-Erling Smørgrav { 1919b7579f77SDag-Erling Smørgrav STACK_OF(X509)* cert; 1920b7579f77SDag-Erling Smørgrav BIO *xml, *p7s; 1921b7579f77SDag-Erling Smørgrav struct ip_list* ip_list = NULL; 1922b7579f77SDag-Erling Smørgrav 1923b7579f77SDag-Erling Smørgrav /* read pem file or provide builtin */ 1924b7579f77SDag-Erling Smørgrav cert = read_cert_or_builtin(root_cert_file); 1925b7579f77SDag-Erling Smørgrav 1926b7579f77SDag-Erling Smørgrav /* lookup A, AAAA for the urlname (or parse urlname if IP address) */ 1927b7579f77SDag-Erling Smørgrav ip_list = resolve_name(urlname, port, res_conf, root_hints, debugconf, 1928b7579f77SDag-Erling Smørgrav ip4only, ip6only); 1929b7579f77SDag-Erling Smørgrav 1930b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK 1931b7579f77SDag-Erling Smørgrav if(1) { /* libunbound finished, startup WSA for the https connection */ 1932b7579f77SDag-Erling Smørgrav WSADATA wsa_data; 1933b7579f77SDag-Erling Smørgrav int r; 1934b7579f77SDag-Erling Smørgrav if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) { 1935b7579f77SDag-Erling Smørgrav if(verb) printf("WSAStartup failed: %s\n", 1936b7579f77SDag-Erling Smørgrav wsa_strerror(r)); 1937b7579f77SDag-Erling Smørgrav exit(0); 1938b7579f77SDag-Erling Smørgrav } 1939b7579f77SDag-Erling Smørgrav atexit(&do_wsa_cleanup); 1940b7579f77SDag-Erling Smørgrav } 1941b7579f77SDag-Erling Smørgrav #endif 1942b7579f77SDag-Erling Smørgrav 1943b7579f77SDag-Erling Smørgrav /* fetch the necessary files over HTTPS */ 1944b7579f77SDag-Erling Smørgrav xml = https(ip_list, xmlname, urlname); 1945b7579f77SDag-Erling Smørgrav p7s = https(ip_list, p7sname, urlname); 1946b7579f77SDag-Erling Smørgrav 1947b7579f77SDag-Erling Smørgrav /* verify and update the root anchor */ 19488ed2b524SDag-Erling Smørgrav verify_and_update_anchor(root_anchor_file, xml, p7s, cert, p7signer); 1949b7579f77SDag-Erling Smørgrav if(verb) printf("success: the anchor has been updated " 1950b7579f77SDag-Erling Smørgrav "using the cert\n"); 1951b7579f77SDag-Erling Smørgrav 1952b7579f77SDag-Erling Smørgrav free_file_bio(xml); 1953b7579f77SDag-Erling Smørgrav free_file_bio(p7s); 1954b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 1955b7579f77SDag-Erling Smørgrav sk_X509_pop_free(cert, X509_free); 1956b7579f77SDag-Erling Smørgrav #endif 1957b7579f77SDag-Erling Smørgrav ip_list_free(ip_list); 1958b7579f77SDag-Erling Smørgrav return 1; 1959b7579f77SDag-Erling Smørgrav } 1960b7579f77SDag-Erling Smørgrav 1961b7579f77SDag-Erling Smørgrav /** 1962b7579f77SDag-Erling Smørgrav * Try to read the root RFC5011 autotrust anchor file, 1963b7579f77SDag-Erling Smørgrav * @param file: filename. 1964b7579f77SDag-Erling Smørgrav * @return: 1965b7579f77SDag-Erling Smørgrav * 0 if does not exist or empty 1966b7579f77SDag-Erling Smørgrav * 1 if trust-point-revoked-5011 1967b7579f77SDag-Erling Smørgrav * 2 if it is OK. 1968b7579f77SDag-Erling Smørgrav */ 1969b7579f77SDag-Erling Smørgrav static int 1970ebc5657fSDag-Erling Smørgrav try_read_anchor(const char* file) 1971b7579f77SDag-Erling Smørgrav { 1972b7579f77SDag-Erling Smørgrav int empty = 1; 1973b7579f77SDag-Erling Smørgrav char line[10240]; 1974b7579f77SDag-Erling Smørgrav char* p; 1975b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 1976b7579f77SDag-Erling Smørgrav if(!in) { 1977b7579f77SDag-Erling Smørgrav /* only if the file does not exist, can we fix it */ 1978b7579f77SDag-Erling Smørgrav if(errno != ENOENT) { 1979b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 1980b7579f77SDag-Erling Smørgrav if(verb) printf("error: cannot access the file\n"); 1981b7579f77SDag-Erling Smørgrav exit(0); 1982b7579f77SDag-Erling Smørgrav } 1983b7579f77SDag-Erling Smørgrav if(verb) printf("%s does not exist\n", file); 1984b7579f77SDag-Erling Smørgrav return 0; 1985b7579f77SDag-Erling Smørgrav } 1986b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 1987b7579f77SDag-Erling Smørgrav line[sizeof(line)-1] = 0; 1988b7579f77SDag-Erling Smørgrav if(strncmp(line, ";;REVOKED", 9) == 0) { 1989b7579f77SDag-Erling Smørgrav fclose(in); 1990b7579f77SDag-Erling Smørgrav if(verb) printf("%s : the trust point is revoked\n" 1991b7579f77SDag-Erling Smørgrav "and the zone is considered unsigned.\n" 1992b7579f77SDag-Erling Smørgrav "if you wish to re-enable, delete the file\n", 1993b7579f77SDag-Erling Smørgrav file); 1994b7579f77SDag-Erling Smørgrav return 1; 1995b7579f77SDag-Erling Smørgrav } 1996b7579f77SDag-Erling Smørgrav p=line; 1997b7579f77SDag-Erling Smørgrav while(*p == ' ' || *p == '\t') 1998b7579f77SDag-Erling Smørgrav p++; 1999b7579f77SDag-Erling Smørgrav if(p[0]==0 || p[0]=='\n' || p[0]==';') continue; 2000b7579f77SDag-Erling Smørgrav /* this line is a line of content */ 2001b7579f77SDag-Erling Smørgrav empty = 0; 2002b7579f77SDag-Erling Smørgrav } 2003b7579f77SDag-Erling Smørgrav fclose(in); 2004b7579f77SDag-Erling Smørgrav if(empty) { 2005b7579f77SDag-Erling Smørgrav if(verb) printf("%s is empty\n", file); 2006b7579f77SDag-Erling Smørgrav return 0; 2007b7579f77SDag-Erling Smørgrav } 2008b7579f77SDag-Erling Smørgrav if(verb) printf("%s has content\n", file); 2009b7579f77SDag-Erling Smørgrav return 2; 2010b7579f77SDag-Erling Smørgrav } 2011b7579f77SDag-Erling Smørgrav 2012b7579f77SDag-Erling Smørgrav /** Write the builtin root anchor to a file */ 2013b7579f77SDag-Erling Smørgrav static void 2014ebc5657fSDag-Erling Smørgrav write_builtin_anchor(const char* file) 2015b7579f77SDag-Erling Smørgrav { 2016b7579f77SDag-Erling Smørgrav const char* builtin_root_anchor = get_builtin_ds(); 2017b7579f77SDag-Erling Smørgrav FILE* out = fopen(file, "w"); 2018b7579f77SDag-Erling Smørgrav if(!out) { 2019b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2020b7579f77SDag-Erling Smørgrav if(verb) printf(" could not write builtin anchor\n"); 2021b7579f77SDag-Erling Smørgrav return; 2022b7579f77SDag-Erling Smørgrav } 2023b7579f77SDag-Erling Smørgrav if(!fwrite(builtin_root_anchor, strlen(builtin_root_anchor), 1, out)) { 2024b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2025b7579f77SDag-Erling Smørgrav if(verb) printf(" could not complete write builtin anchor\n"); 2026b7579f77SDag-Erling Smørgrav } 2027b7579f77SDag-Erling Smørgrav fclose(out); 2028b7579f77SDag-Erling Smørgrav } 2029b7579f77SDag-Erling Smørgrav 2030b7579f77SDag-Erling Smørgrav /** 2031b7579f77SDag-Erling Smørgrav * Check the root anchor file. 2032b7579f77SDag-Erling Smørgrav * If does not exist, provide builtin and write file. 2033b7579f77SDag-Erling Smørgrav * If empty, provide builtin and write file. 2034b7579f77SDag-Erling Smørgrav * If trust-point-revoked-5011 file: make the program exit. 2035b7579f77SDag-Erling Smørgrav * @param root_anchor_file: filename of the root anchor. 2036b7579f77SDag-Erling Smørgrav * @param used_builtin: set to 1 if the builtin is written. 2037b7579f77SDag-Erling Smørgrav * @return 0 if trustpoint is insecure, 1 on success. Exit on failure. 2038b7579f77SDag-Erling Smørgrav */ 2039b7579f77SDag-Erling Smørgrav static int 2040ebc5657fSDag-Erling Smørgrav provide_builtin(const char* root_anchor_file, int* used_builtin) 2041b7579f77SDag-Erling Smørgrav { 2042b7579f77SDag-Erling Smørgrav /* try to read it */ 2043b7579f77SDag-Erling Smørgrav switch(try_read_anchor(root_anchor_file)) 2044b7579f77SDag-Erling Smørgrav { 2045b7579f77SDag-Erling Smørgrav case 0: /* no exist or empty */ 2046b7579f77SDag-Erling Smørgrav write_builtin_anchor(root_anchor_file); 2047b7579f77SDag-Erling Smørgrav *used_builtin = 1; 2048b7579f77SDag-Erling Smørgrav break; 2049b7579f77SDag-Erling Smørgrav case 1: /* revoked tp */ 2050b7579f77SDag-Erling Smørgrav return 0; 2051b7579f77SDag-Erling Smørgrav case 2: /* it is fine */ 2052b7579f77SDag-Erling Smørgrav default: 2053b7579f77SDag-Erling Smørgrav break; 2054b7579f77SDag-Erling Smørgrav } 2055b7579f77SDag-Erling Smørgrav return 1; 2056b7579f77SDag-Erling Smørgrav } 2057b7579f77SDag-Erling Smørgrav 2058b7579f77SDag-Erling Smørgrav /** 2059b7579f77SDag-Erling Smørgrav * add an autotrust anchor for the root to the context 2060b7579f77SDag-Erling Smørgrav */ 2061b7579f77SDag-Erling Smørgrav static void 2062ebc5657fSDag-Erling Smørgrav add_5011_probe_root(struct ub_ctx* ctx, const char* root_anchor_file) 2063b7579f77SDag-Erling Smørgrav { 2064b7579f77SDag-Erling Smørgrav int r; 2065b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "auto-trust-anchor-file:", root_anchor_file); 2066b7579f77SDag-Erling Smørgrav if(r) { 2067b7579f77SDag-Erling Smørgrav if(verb) printf("add 5011 probe to ctx: %s\n", ub_strerror(r)); 2068b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 2069b7579f77SDag-Erling Smørgrav exit(0); 2070b7579f77SDag-Erling Smørgrav } 2071b7579f77SDag-Erling Smørgrav } 2072b7579f77SDag-Erling Smørgrav 2073b7579f77SDag-Erling Smørgrav /** 2074b7579f77SDag-Erling Smørgrav * Prime the root key and return the result. Exit on error. 2075b7579f77SDag-Erling Smørgrav * @param ctx: the unbound context to perform the priming with. 2076b7579f77SDag-Erling Smørgrav * @return: the result of the prime, on error it exit()s. 2077b7579f77SDag-Erling Smørgrav */ 2078b7579f77SDag-Erling Smørgrav static struct ub_result* 2079b7579f77SDag-Erling Smørgrav prime_root_key(struct ub_ctx* ctx) 2080b7579f77SDag-Erling Smørgrav { 2081b7579f77SDag-Erling Smørgrav struct ub_result* res = NULL; 2082b7579f77SDag-Erling Smørgrav int r; 2083b7579f77SDag-Erling Smørgrav r = ub_resolve(ctx, ".", LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &res); 2084b7579f77SDag-Erling Smørgrav if(r) { 2085b7579f77SDag-Erling Smørgrav if(verb) printf("resolve DNSKEY: %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 if(!res) { 2090b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 2091b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 2092b7579f77SDag-Erling Smørgrav exit(0); 2093b7579f77SDag-Erling Smørgrav } 2094b7579f77SDag-Erling Smørgrav return res; 2095b7579f77SDag-Erling Smørgrav } 2096b7579f77SDag-Erling Smørgrav 2097b7579f77SDag-Erling Smørgrav /** see if ADDPEND keys exist in autotrust file (if possible) */ 2098b7579f77SDag-Erling Smørgrav static int 2099ebc5657fSDag-Erling Smørgrav read_if_pending_keys(const char* file) 2100b7579f77SDag-Erling Smørgrav { 2101b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 2102b7579f77SDag-Erling Smørgrav char line[8192]; 2103b7579f77SDag-Erling Smørgrav if(!in) { 2104b7579f77SDag-Erling Smørgrav if(verb>=2) printf("%s: %s\n", file, strerror(errno)); 2105b7579f77SDag-Erling Smørgrav return 0; 2106b7579f77SDag-Erling Smørgrav } 2107b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 2108b7579f77SDag-Erling Smørgrav if(line[0]==';') continue; 2109b7579f77SDag-Erling Smørgrav if(strstr(line, "[ ADDPEND ]")) { 2110b7579f77SDag-Erling Smørgrav fclose(in); 2111b7579f77SDag-Erling Smørgrav if(verb) printf("RFC5011-state has ADDPEND keys\n"); 2112b7579f77SDag-Erling Smørgrav return 1; 2113b7579f77SDag-Erling Smørgrav } 2114b7579f77SDag-Erling Smørgrav } 2115b7579f77SDag-Erling Smørgrav fclose(in); 2116b7579f77SDag-Erling Smørgrav return 0; 2117b7579f77SDag-Erling Smørgrav } 2118b7579f77SDag-Erling Smørgrav 2119b7579f77SDag-Erling Smørgrav /** read last successful probe time from autotrust file (if possible) */ 2120b7579f77SDag-Erling Smørgrav static int32_t 2121ebc5657fSDag-Erling Smørgrav read_last_success_time(const char* file) 2122b7579f77SDag-Erling Smørgrav { 2123b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 2124b7579f77SDag-Erling Smørgrav char line[1024]; 2125b7579f77SDag-Erling Smørgrav if(!in) { 2126b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2127b7579f77SDag-Erling Smørgrav return 0; 2128b7579f77SDag-Erling Smørgrav } 2129b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 2130b7579f77SDag-Erling Smørgrav if(strncmp(line, ";;last_success: ", 16) == 0) { 2131b7579f77SDag-Erling Smørgrav char* e; 2132b7579f77SDag-Erling Smørgrav time_t x = (unsigned int)strtol(line+16, &e, 10); 2133b7579f77SDag-Erling Smørgrav fclose(in); 2134b7579f77SDag-Erling Smørgrav if(line+16 == e) { 2135b7579f77SDag-Erling Smørgrav if(verb) printf("failed to parse " 2136b7579f77SDag-Erling Smørgrav "last_success probe time\n"); 2137b7579f77SDag-Erling Smørgrav return 0; 2138b7579f77SDag-Erling Smørgrav } 2139b7579f77SDag-Erling Smørgrav if(verb) printf("last successful probe: %s", ctime(&x)); 2140b7579f77SDag-Erling Smørgrav return (int32_t)x; 2141b7579f77SDag-Erling Smørgrav } 2142b7579f77SDag-Erling Smørgrav } 2143b7579f77SDag-Erling Smørgrav fclose(in); 2144b7579f77SDag-Erling Smørgrav if(verb) printf("no last_success probe time in anchor file\n"); 2145b7579f77SDag-Erling Smørgrav return 0; 2146b7579f77SDag-Erling Smørgrav } 2147b7579f77SDag-Erling Smørgrav 2148b7579f77SDag-Erling Smørgrav /** 2149b7579f77SDag-Erling Smørgrav * Read autotrust 5011 probe file and see if the date 2150b7579f77SDag-Erling Smørgrav * compared to the current date allows a certupdate. 2151b7579f77SDag-Erling Smørgrav * If the last successful probe was recent then 5011 cannot be behind, 2152b7579f77SDag-Erling Smørgrav * and the failure cannot be solved with a certupdate. 2153b7579f77SDag-Erling Smørgrav * The debugconf is to validation-override the date for testing. 2154b7579f77SDag-Erling Smørgrav * @param root_anchor_file: filename of root key 2155b7579f77SDag-Erling Smørgrav * @return true if certupdate is ok. 2156b7579f77SDag-Erling Smørgrav */ 2157b7579f77SDag-Erling Smørgrav static int 2158ebc5657fSDag-Erling Smørgrav probe_date_allows_certupdate(const char* root_anchor_file) 2159b7579f77SDag-Erling Smørgrav { 2160b7579f77SDag-Erling Smørgrav int has_pending_keys = read_if_pending_keys(root_anchor_file); 2161b7579f77SDag-Erling Smørgrav int32_t last_success = read_last_success_time(root_anchor_file); 2162b7579f77SDag-Erling Smørgrav int32_t now = (int32_t)time(NULL); 2163b7579f77SDag-Erling Smørgrav int32_t leeway = 30 * 24 * 3600; /* 30 days leeway */ 2164b7579f77SDag-Erling Smørgrav /* if the date is before 2010-07-15:00.00.00 then the root has not 2165b7579f77SDag-Erling Smørgrav * been signed yet, and thus we refuse to take action. */ 2166b7579f77SDag-Erling Smørgrav if(time(NULL) < xml_convertdate("2010-07-15T00:00:00")) { 2167b7579f77SDag-Erling Smørgrav if(verb) printf("the date is before the root was first signed," 2168b7579f77SDag-Erling Smørgrav " please correct the clock\n"); 2169b7579f77SDag-Erling Smørgrav return 0; 2170b7579f77SDag-Erling Smørgrav } 2171b7579f77SDag-Erling Smørgrav if(last_success == 0) 2172b7579f77SDag-Erling Smørgrav return 1; /* no probe time */ 2173b7579f77SDag-Erling Smørgrav if(has_pending_keys) 2174b7579f77SDag-Erling Smørgrav return 1; /* key in ADDPEND state, a previous probe has 2175b7579f77SDag-Erling Smørgrav inserted that, and it was present in all recent probes, 2176b7579f77SDag-Erling Smørgrav but it has not become active. The 30 day timer may not have 2177b7579f77SDag-Erling Smørgrav expired, but we know(for sure) there is a rollover going on. 2178b7579f77SDag-Erling Smørgrav If we only managed to pickup the new key on its last day 2179b7579f77SDag-Erling Smørgrav of announcement (for example) this can happen. */ 2180b7579f77SDag-Erling Smørgrav if(now - last_success < 0) { 2181b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe is in the future," 2182b7579f77SDag-Erling Smørgrav " clock was modified\n"); 2183b7579f77SDag-Erling Smørgrav return 0; 2184b7579f77SDag-Erling Smørgrav } 2185b7579f77SDag-Erling Smørgrav if(now - last_success >= leeway) { 2186b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe was more than 30 " 2187b7579f77SDag-Erling Smørgrav "days ago\n"); 2188b7579f77SDag-Erling Smørgrav return 1; 2189b7579f77SDag-Erling Smørgrav } 2190b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe is recent\n"); 2191b7579f77SDag-Erling Smørgrav return 0; 2192b7579f77SDag-Erling Smørgrav } 2193b7579f77SDag-Erling Smørgrav 2194*4c75e3aaSDag-Erling Smørgrav static struct ub_result * 2195*4c75e3aaSDag-Erling Smørgrav fetch_root_key(const char* root_anchor_file, const char* res_conf, 2196*4c75e3aaSDag-Erling Smørgrav const char* root_hints, const char* debugconf, 2197*4c75e3aaSDag-Erling Smørgrav int ip4only, int ip6only) 2198*4c75e3aaSDag-Erling Smørgrav { 2199*4c75e3aaSDag-Erling Smørgrav struct ub_ctx* ctx; 2200*4c75e3aaSDag-Erling Smørgrav struct ub_result* dnskey; 2201*4c75e3aaSDag-Erling Smørgrav 2202*4c75e3aaSDag-Erling Smørgrav ctx = create_unbound_context(res_conf, root_hints, debugconf, 2203*4c75e3aaSDag-Erling Smørgrav ip4only, ip6only); 2204*4c75e3aaSDag-Erling Smørgrav add_5011_probe_root(ctx, root_anchor_file); 2205*4c75e3aaSDag-Erling Smørgrav dnskey = prime_root_key(ctx); 2206*4c75e3aaSDag-Erling Smørgrav ub_ctx_delete(ctx); 2207*4c75e3aaSDag-Erling Smørgrav return dnskey; 2208*4c75e3aaSDag-Erling Smørgrav } 2209*4c75e3aaSDag-Erling Smørgrav 2210b7579f77SDag-Erling Smørgrav /** perform the unbound-anchor work */ 2211b7579f77SDag-Erling Smørgrav static int 2212ebc5657fSDag-Erling Smørgrav do_root_update_work(const char* root_anchor_file, const char* root_cert_file, 2213ebc5657fSDag-Erling Smørgrav const char* urlname, const char* xmlname, const char* p7sname, 2214ebc5657fSDag-Erling Smørgrav const char* p7signer, const char* res_conf, const char* root_hints, 2215*4c75e3aaSDag-Erling Smørgrav const char* debugconf, int ip4only, int ip6only, int force, 2216*4c75e3aaSDag-Erling Smørgrav int res_conf_fallback, int port) 2217b7579f77SDag-Erling Smørgrav { 2218b7579f77SDag-Erling Smørgrav struct ub_result* dnskey; 2219b7579f77SDag-Erling Smørgrav int used_builtin = 0; 2220*4c75e3aaSDag-Erling Smørgrav int rcode; 2221b7579f77SDag-Erling Smørgrav 2222b7579f77SDag-Erling Smørgrav /* see if builtin rootanchor needs to be provided, or if 2223b7579f77SDag-Erling Smørgrav * rootanchor is 'revoked-trust-point' */ 2224b7579f77SDag-Erling Smørgrav if(!provide_builtin(root_anchor_file, &used_builtin)) 2225b7579f77SDag-Erling Smørgrav return 0; 2226b7579f77SDag-Erling Smørgrav 2227b7579f77SDag-Erling Smørgrav /* make unbound context with 5011-probe for root anchor, 2228b7579f77SDag-Erling Smørgrav * and probe . DNSKEY */ 2229*4c75e3aaSDag-Erling Smørgrav dnskey = fetch_root_key(root_anchor_file, res_conf, 2230*4c75e3aaSDag-Erling Smørgrav root_hints, debugconf, ip4only, ip6only); 2231*4c75e3aaSDag-Erling Smørgrav rcode = dnskey->rcode; 2232*4c75e3aaSDag-Erling Smørgrav 2233*4c75e3aaSDag-Erling Smørgrav if (res_conf_fallback && res_conf && !dnskey->secure) { 2234*4c75e3aaSDag-Erling Smørgrav if (verb) printf("%s failed, retrying direct\n", res_conf); 2235*4c75e3aaSDag-Erling Smørgrav ub_resolve_free(dnskey); 2236*4c75e3aaSDag-Erling Smørgrav /* try direct query without res_conf */ 2237*4c75e3aaSDag-Erling Smørgrav dnskey = fetch_root_key(root_anchor_file, NULL, 2238*4c75e3aaSDag-Erling Smørgrav root_hints, debugconf, ip4only, ip6only); 2239*4c75e3aaSDag-Erling Smørgrav if (rcode != 0 && dnskey->rcode == 0) { 2240*4c75e3aaSDag-Erling Smørgrav res_conf = NULL; 2241*4c75e3aaSDag-Erling Smørgrav rcode = 0; 2242*4c75e3aaSDag-Erling Smørgrav } 2243*4c75e3aaSDag-Erling Smørgrav } 2244b7579f77SDag-Erling Smørgrav 2245b7579f77SDag-Erling Smørgrav /* if secure: exit */ 2246b7579f77SDag-Erling Smørgrav if(dnskey->secure && !force) { 2247b7579f77SDag-Erling Smørgrav if(verb) printf("success: the anchor is ok\n"); 2248b7579f77SDag-Erling Smørgrav ub_resolve_free(dnskey); 2249b7579f77SDag-Erling Smørgrav return used_builtin; 2250b7579f77SDag-Erling Smørgrav } 2251b7579f77SDag-Erling Smørgrav if(force && verb) printf("debug cert update forced\n"); 2252*4c75e3aaSDag-Erling Smørgrav ub_resolve_free(dnskey); 2253b7579f77SDag-Erling Smørgrav 2254b7579f77SDag-Erling Smørgrav /* if not (and NOERROR): check date and do certupdate */ 2255*4c75e3aaSDag-Erling Smørgrav if((rcode == 0 && 2256b7579f77SDag-Erling Smørgrav probe_date_allows_certupdate(root_anchor_file)) || force) { 2257b7579f77SDag-Erling Smørgrav if(do_certupdate(root_anchor_file, root_cert_file, urlname, 22588ed2b524SDag-Erling Smørgrav xmlname, p7sname, p7signer, res_conf, root_hints, 2259*4c75e3aaSDag-Erling Smørgrav debugconf, ip4only, ip6only, port)) 2260b7579f77SDag-Erling Smørgrav return 1; 2261b7579f77SDag-Erling Smørgrav return used_builtin; 2262b7579f77SDag-Erling Smørgrav } 2263b7579f77SDag-Erling Smørgrav if(verb) printf("fail: the anchor is NOT ok and could not be fixed\n"); 2264b7579f77SDag-Erling Smørgrav return used_builtin; 2265b7579f77SDag-Erling Smørgrav } 2266b7579f77SDag-Erling Smørgrav 2267b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */ 2268b7579f77SDag-Erling Smørgrav extern int optind; 2269b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */ 2270b7579f77SDag-Erling Smørgrav extern char* optarg; 2271b7579f77SDag-Erling Smørgrav 2272b7579f77SDag-Erling Smørgrav /** Main routine for unbound-anchor */ 2273b7579f77SDag-Erling Smørgrav int main(int argc, char* argv[]) 2274b7579f77SDag-Erling Smørgrav { 2275b7579f77SDag-Erling Smørgrav int c; 2276ebc5657fSDag-Erling Smørgrav const char* root_anchor_file = ROOT_ANCHOR_FILE; 2277ebc5657fSDag-Erling Smørgrav const char* root_cert_file = ROOT_CERT_FILE; 2278ebc5657fSDag-Erling Smørgrav const char* urlname = URLNAME; 2279ebc5657fSDag-Erling Smørgrav const char* xmlname = XMLNAME; 2280ebc5657fSDag-Erling Smørgrav const char* p7sname = P7SNAME; 2281ebc5657fSDag-Erling Smørgrav const char* p7signer = P7SIGNER; 2282ebc5657fSDag-Erling Smørgrav const char* res_conf = NULL; 2283ebc5657fSDag-Erling Smørgrav const char* root_hints = NULL; 2284ebc5657fSDag-Erling Smørgrav const char* debugconf = NULL; 2285b7579f77SDag-Erling Smørgrav int dolist=0, ip4only=0, ip6only=0, force=0, port = HTTPS_PORT; 2286*4c75e3aaSDag-Erling Smørgrav int res_conf_fallback = 0; 2287b7579f77SDag-Erling Smørgrav /* parse the options */ 2288*4c75e3aaSDag-Erling Smørgrav while( (c=getopt(argc, argv, "46C:FRP:a:c:f:hln:r:s:u:vx:")) != -1) { 2289b7579f77SDag-Erling Smørgrav switch(c) { 2290b7579f77SDag-Erling Smørgrav case 'l': 2291b7579f77SDag-Erling Smørgrav dolist = 1; 2292b7579f77SDag-Erling Smørgrav break; 2293b7579f77SDag-Erling Smørgrav case '4': 2294b7579f77SDag-Erling Smørgrav ip4only = 1; 2295b7579f77SDag-Erling Smørgrav break; 2296b7579f77SDag-Erling Smørgrav case '6': 2297b7579f77SDag-Erling Smørgrav ip6only = 1; 2298b7579f77SDag-Erling Smørgrav break; 2299b7579f77SDag-Erling Smørgrav case 'a': 2300b7579f77SDag-Erling Smørgrav root_anchor_file = optarg; 2301b7579f77SDag-Erling Smørgrav break; 2302b7579f77SDag-Erling Smørgrav case 'c': 2303b7579f77SDag-Erling Smørgrav root_cert_file = optarg; 2304b7579f77SDag-Erling Smørgrav break; 2305b7579f77SDag-Erling Smørgrav case 'u': 2306b7579f77SDag-Erling Smørgrav urlname = optarg; 2307b7579f77SDag-Erling Smørgrav break; 2308b7579f77SDag-Erling Smørgrav case 'x': 2309b7579f77SDag-Erling Smørgrav xmlname = optarg; 2310b7579f77SDag-Erling Smørgrav break; 2311b7579f77SDag-Erling Smørgrav case 's': 2312b7579f77SDag-Erling Smørgrav p7sname = optarg; 2313b7579f77SDag-Erling Smørgrav break; 23148ed2b524SDag-Erling Smørgrav case 'n': 23158ed2b524SDag-Erling Smørgrav p7signer = optarg; 23168ed2b524SDag-Erling Smørgrav break; 2317b7579f77SDag-Erling Smørgrav case 'f': 2318b7579f77SDag-Erling Smørgrav res_conf = optarg; 2319b7579f77SDag-Erling Smørgrav break; 2320b7579f77SDag-Erling Smørgrav case 'r': 2321b7579f77SDag-Erling Smørgrav root_hints = optarg; 2322b7579f77SDag-Erling Smørgrav break; 2323*4c75e3aaSDag-Erling Smørgrav case 'R': 2324*4c75e3aaSDag-Erling Smørgrav res_conf_fallback = 1; 2325*4c75e3aaSDag-Erling Smørgrav break; 2326b7579f77SDag-Erling Smørgrav case 'C': 2327b7579f77SDag-Erling Smørgrav debugconf = optarg; 2328b7579f77SDag-Erling Smørgrav break; 2329b7579f77SDag-Erling Smørgrav case 'F': 2330b7579f77SDag-Erling Smørgrav force = 1; 2331b7579f77SDag-Erling Smørgrav break; 2332b7579f77SDag-Erling Smørgrav case 'P': 2333b7579f77SDag-Erling Smørgrav port = atoi(optarg); 2334b7579f77SDag-Erling Smørgrav break; 2335b7579f77SDag-Erling Smørgrav case 'v': 2336b7579f77SDag-Erling Smørgrav verb++; 2337b7579f77SDag-Erling Smørgrav break; 2338b7579f77SDag-Erling Smørgrav case '?': 2339b7579f77SDag-Erling Smørgrav case 'h': 2340b7579f77SDag-Erling Smørgrav default: 2341b7579f77SDag-Erling Smørgrav usage(); 2342b7579f77SDag-Erling Smørgrav } 2343b7579f77SDag-Erling Smørgrav } 2344b7579f77SDag-Erling Smørgrav argc -= optind; 2345b7579f77SDag-Erling Smørgrav argv += optind; 2346b7579f77SDag-Erling Smørgrav if(argc != 0) 2347b7579f77SDag-Erling Smørgrav usage(); 2348b7579f77SDag-Erling Smørgrav 2349b5663de9SDag-Erling Smørgrav #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS 2350b7579f77SDag-Erling Smørgrav ERR_load_crypto_strings(); 2351b5663de9SDag-Erling Smørgrav #endif 2352971980c3SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 2353b7579f77SDag-Erling Smørgrav ERR_load_SSL_strings(); 2354971980c3SDag-Erling Smørgrav #endif 2355b5663de9SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) 2356b7579f77SDag-Erling Smørgrav OpenSSL_add_all_algorithms(); 2357b5663de9SDag-Erling Smørgrav #else 2358b5663de9SDag-Erling Smørgrav OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 2359b5663de9SDag-Erling Smørgrav | OPENSSL_INIT_ADD_ALL_DIGESTS 2360b5663de9SDag-Erling Smørgrav | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 2361b5663de9SDag-Erling Smørgrav #endif 2362b5663de9SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 2363b7579f77SDag-Erling Smørgrav (void)SSL_library_init(); 2364b5663de9SDag-Erling Smørgrav #else 2365971980c3SDag-Erling Smørgrav (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 2366b5663de9SDag-Erling Smørgrav #endif 2367b7579f77SDag-Erling Smørgrav 2368b7579f77SDag-Erling Smørgrav if(dolist) do_list_builtin(); 2369b7579f77SDag-Erling Smørgrav 2370b7579f77SDag-Erling Smørgrav return do_root_update_work(root_anchor_file, root_cert_file, urlname, 23718ed2b524SDag-Erling Smørgrav xmlname, p7sname, p7signer, res_conf, root_hints, debugconf, 2372*4c75e3aaSDag-Erling Smørgrav ip4only, ip6only, force, res_conf_fallback, port); 2373b7579f77SDag-Erling Smørgrav } 2374