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