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