blob: 4bcfb1d3bfc36b43e5ca7a1f0211c0ee773ca53a [file] [log] [blame]
Christian Grothoff90e9f832012-02-07 11:16:47 +01001/* Copyright (c) 2012, Jacob Appelbaum.
Jacob Appelbaum63e548e2012-07-29 16:25:30 -07002 * Copyright (c) 2012, The Tor Project, Inc.
3 * Copyright (c) 2012, Christian Grothoff. */
Christian Grothoff90e9f832012-02-07 11:16:47 +01004/* See LICENSE for licensing information */
5/*
6 This file contains the license for tlsdate,
7 a free software project to set your system clock securely.
8
9 It also lists the licenses for other components used by tlsdate.
10
11 For more information about tlsdate, see https://github.com/ioerror/tlsdate
12
13 If you got this file as a part of a larger bundle,
14 there may be other license terms that you should be aware of.
15
16===============================================================================
17tlsdate is distributed under this license:
18
19Copyright (c) 2011-2012, Jacob Appelbaum <jacob@appelbaum.net>
20Copyright (c) 2011-2012, The Tor Project, Inc.
21
22Redistribution and use in source and binary forms, with or without
23modification, are permitted provided that the following conditions are
24met:
25
26 * Redistributions of source code must retain the above copyright
27notice, this list of conditions and the following disclaimer.
28
29 * Redistributions in binary form must reproduce the above
30copyright notice, this list of conditions and the following disclaimer
31in the documentation and/or other materials provided with the
32distribution.
33
34 * Neither the names of the copyright owners nor the names of its
35contributors may be used to endorse or promote products derived from
36this software without specific prior written permission.
37
38THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
41A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
42OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49===============================================================================
50If you got tlsdate as a static binary with OpenSSL included, then you should
51know:
52
53 "This product includes software developed by the OpenSSL Project for use in
54 the OpenSSL Toolkit (http://www.openssl.org/)"
55
56===============================================================================
57*/
58
59/**
Christian Grothoffe267c352012-02-14 01:10:54 +010060 * \file tlsdate-helper.c
61 * \brief Helper program that does the actual work of setting the system clock.
Christian Grothoff90e9f832012-02-07 11:16:47 +010062 **/
63
64/*
65 * tlsdate is a tool for setting the system clock by hand or by communication
66 * with the network. It does not set the RTC. It is designed to be as secure as
67 * TLS (RFC 2246) but of course the security of TLS is often reduced to
68 * whichever CA racket you believe is trustworthy. By default, tlsdate trusts
69 * your local CA root store - so any of these companies could assist in a MITM
70 * attack against you and you'd be screwed.
71
72 * This tool is designed to be run by hand or as a system daemon. It must be
73 * run as root or otherwise have the proper caps; it will not be able to set
74 * the system time without running as root or another privileged user.
75 */
76
Brian Akerb12abad2012-10-16 01:25:00 -040077#include "config.h"
Andreea Costinas6d6944a2019-03-27 15:29:09 +010078
Daniel Kurtza2d82d52019-06-07 15:26:54 -060079#include "src/openssl_compat.h"
Brian Akerb12abad2012-10-16 01:25:00 -040080#include "src/tlsdate-helper.h"
Christian Grothoff90e9f832012-02-07 11:16:47 +010081
Andreea Costinas6d6944a2019-03-27 15:29:09 +010082#include <errno.h>
83#include <netdb.h>
84#include <stdlib.h>
85#include <sys/types.h>
86#include <sys/socket.h>
87
Jacob Appelbaumfa765302013-01-18 15:47:47 +010088#include "src/proxy-bio.h"
Ben Chancc6af4b2019-04-11 14:13:08 -070089#include "src/util.h"
Brian Akerb12abad2012-10-16 01:25:00 -040090#include "src/compat/clock.h"
Elly Jones9ae3aa62012-06-20 15:32:46 -040091
Andreea Costinas6d6944a2019-03-27 15:29:09 +010092static int g_ca_racket;
93
94static const char *g_ca_cert_container;
95
96// Target host to connect to.
97static const char *g_host;
98
99// Port to connect to on the target host.
100static const char *g_port;
101
102// The SSL/TLS protocol used
103static const char *g_protocol;
104
105// Proxy with format scheme://proxy_host:proxy_port.
106static char *g_proxy;
107
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400108static void
Will Drewryc45952f2013-09-03 13:51:24 -0500109validate_proxy_scheme (const char *scheme)
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400110{
Will Drewryc45952f2013-09-03 13:51:24 -0500111 if (!strcmp (scheme, "http"))
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400112 return;
Will Drewryc45952f2013-09-03 13:51:24 -0500113 if (!strcmp (scheme, "socks4"))
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400114 return;
Will Drewryc45952f2013-09-03 13:51:24 -0500115 if (!strcmp (scheme, "socks5"))
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400116 return;
Will Drewryc45952f2013-09-03 13:51:24 -0500117 die ("invalid proxy scheme\n");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400118}
119
120static void
Will Drewryc45952f2013-09-03 13:51:24 -0500121validate_proxy_host (const char *host)
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400122{
123 const char *kValid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
124 "abcdefghijklmnopqrstuvwxyz"
125 "0123456789"
126 ".-";
Will Drewryc45952f2013-09-03 13:51:24 -0500127 if (strspn (host, kValid) != strlen (host))
128 die ("invalid char in host\n");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400129}
130
131static void
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100132validate_port (const char *port)
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400133{
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100134 if (!port)
135 return;
136 char *end;
137 const int kBase = 10;
Andreea Costinas95e4d2c2021-06-08 14:05:02 +0200138 errno = 0;
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100139 unsigned long value = strtoul(port, &end, kBase);
Andreea Costinas95e4d2c2021-06-08 14:05:02 +0200140 if (errno != 0 || value > USHRT_MAX || value == 0 || *end != '\0')
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100141 die("invalid port %s\n", port);
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400142}
143
144static void
Will Drewryc45952f2013-09-03 13:51:24 -0500145parse_proxy_uri (char *proxy, char **scheme, char **host, char **port)
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400146{
147 /* Expecting a URI, so: <scheme> '://' <host> ':' <port> */
148 *scheme = proxy;
Will Drewryc45952f2013-09-03 13:51:24 -0500149 proxy = strstr (proxy, "://");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400150 if (!proxy)
Will Drewryc45952f2013-09-03 13:51:24 -0500151 die ("malformed proxy URI\n");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400152 *proxy = '\0'; /* terminate scheme string */
Will Drewryc45952f2013-09-03 13:51:24 -0500153 proxy += strlen ("://");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400154 *host = proxy;
Will Drewryc45952f2013-09-03 13:51:24 -0500155 proxy = strchr (proxy, ':');
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400156 if (!proxy)
Will Drewryc45952f2013-09-03 13:51:24 -0500157 die ("malformed proxy URI\n");
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400158 *proxy++ = '\0';
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400159 *port = proxy;
Will Drewryc45952f2013-09-03 13:51:24 -0500160 validate_proxy_scheme (*scheme);
161 validate_proxy_host (*host);
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100162 validate_port (*port);
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400163}
164
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100165/* Returns a BIO that talks through a HTTP proxy using the CONNECT command.
166 * The created BIO will ask the proxy to CONNECT to |target_host|:|target_port|
167 * using |scheme|.
168 */
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400169static BIO *
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100170BIO_create_proxy (const char *scheme, const char *target_host,
171 const char *target_port)
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400172{
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100173 BIO *bio_proxy = BIO_new_proxy ();
174 BIO_proxy_set_type (bio_proxy, scheme);
175 BIO_proxy_set_target_host (bio_proxy, target_host);
176 BIO_proxy_set_target_port (bio_proxy, atoi (target_port));
177 return bio_proxy;
178}
179
Pavol Markod9dd19d2020-01-17 23:32:57 +0100180static void
181addr_to_str (const struct sockaddr *addr, char* dest,
182 socklen_t size)
183{
184 struct sockaddr_in* addr_ipv4 = NULL;
185 struct sockaddr_in6* addr_ipv6 = NULL;
186 memset (dest, '\0', size);
187 if (addr->sa_family == AF_INET) {
188 addr_ipv4 = (struct sockaddr_in*)addr;
189 inet_ntop (AF_INET, &addr_ipv4->sin_addr, dest, size);
190 return;
191 }
192 if (addr->sa_family == AF_INET6) {
193 addr_ipv6 = (struct sockaddr_in6*)addr;
194 inet_ntop (AF_INET6, &addr_ipv6->sin6_addr, dest, size);
195 return;
196 }
197 verb ("V: unknown sa_family %hu\n", addr->sa_family);
198}
199
Michael Ershov2012e5b2021-03-25 13:18:13 +0100200static int
201get_addrinfo_length (struct addrinfo *addr_infos) {
202 int result = 0;
203 for (struct addrinfo *iter = addr_infos; iter; iter = iter->ai_next) {
204 ++result;
205 }
206 return result;
207}
208
209static struct addrinfo *
210get_addrinfo_element (struct addrinfo *addr_infos, int index) {
211 int current_index = 0;
212 for (struct addrinfo *iter = addr_infos; iter; iter = iter->ai_next) {
213 if (current_index == index) {
214 return iter;
215 }
216 ++current_index;
217 }
218 return NULL;
219}
220
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100221/* Connects to |host| on port |port|.
222 * Returns the socket file descriptor if successful, otherwise exits with
223 * failure.
224 */
Michael Ershov2012e5b2021-03-25 13:18:13 +0100225static int
226create_connection (const char *host, const char *port)
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100227{
Michael Ershov2012e5b2021-03-25 13:18:13 +0100228 int sock = -1;
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100229 struct addrinfo hints = {
230 .ai_flags = AI_ADDRCONFIG,
231 .ai_family = AF_UNSPEC,
232 .ai_socktype = SOCK_STREAM,
233 };
Pavol Markod9dd19d2020-01-17 23:32:57 +0100234 // Use INET6_ADDRSTRLEN for the buffer holding IP addresses as it will always
235 // be longer than INET_ADDRSTRLEN.
236 char addr_str_buf[INET6_ADDRSTRLEN];
237 memset (addr_str_buf, '\0', INET6_ADDRSTRLEN);
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100238
Michael Ershov2012e5b2021-03-25 13:18:13 +0100239 struct addrinfo *addr_infos = NULL;
240 int err = getaddrinfo (host, port, &hints, &addr_infos);
241 if (err != 0 || !addr_infos)
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100242 die ("getaddrinfo (%s): %s\n", host, gai_strerror (err));
243
Michael Ershov5fb1b3b2021-04-30 13:47:40 +0200244 // tlsdate is killed by a supervisor if it takes too long to finish. So it
245 // may not have time to try all the addresses. Selecting the start point
246 // randomly makes it try different addresses during different attempts, that
247 // is useful when some addresses are not accessible (e.g. first ones).
Michael Ershov2012e5b2021-03-25 13:18:13 +0100248 int list_length = get_addrinfo_length (addr_infos);
Michael Ershov5fb1b3b2021-04-30 13:47:40 +0200249 int start_index = random () % list_length;
250
Michael Ershov2012e5b2021-03-25 13:18:13 +0100251 for (int i = 0; i < list_length; ++i)
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100252 {
Michael Ershov2012e5b2021-03-25 13:18:13 +0100253 struct addrinfo *current_addr_info =
Michael Ershov5fb1b3b2021-04-30 13:47:40 +0200254 get_addrinfo_element (addr_infos, (start_index + i) % list_length);
Michael Ershov2012e5b2021-03-25 13:18:13 +0100255 if (!current_addr_info) {
256 die ("attempted to use NULL addrinfo");
257 }
258
259 addr_to_str (current_addr_info->ai_addr, addr_str_buf, INET6_ADDRSTRLEN);
Pavol Markod9dd19d2020-01-17 23:32:57 +0100260 verb ("V: attempting to connect to %s\n", addr_str_buf);
Michael Ershov2012e5b2021-03-25 13:18:13 +0100261 sock = socket (current_addr_info->ai_family, SOCK_STREAM, 0);
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100262 if (sock < 0)
263 {
264 perror ("socket");
265 continue;
266 }
267
Michael Ershov2012e5b2021-03-25 13:18:13 +0100268 if (connect (sock, current_addr_info->ai_addr,
269 current_addr_info->ai_addrlen) != 0)
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100270 {
271 perror ("connect");
272 close (sock);
273 sock = -1;
274 continue;
275 }
276 break;
277 }
Michael Ershov2012e5b2021-03-25 13:18:13 +0100278 freeaddrinfo (addr_infos);
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100279
280 if (sock < 0)
281 die ("failed to find any remote addresses for %s:%s\n", host, port);
282
283 return sock;
284}
285
286/* Creates a BIO wrapper over the socket connection to |host|:|port|.
287 * This workaround is needed because BIO_s_connect() doesn't support IPv6.
288 */
289static BIO *BIO_create_socket (const char *host, const char *port)
290{
291 BIO *bio_socket = NULL;
292 int sockfd = create_connection (host, port);
293 if ( !(bio_socket = BIO_new_fd (sockfd, 1 /* close_flag */)))
294 die ("BIO_new_fd failed\n");
295
296 return bio_socket;
297}
298
299/* Creates an OpenSSL BIO-chain which talks to |target_host|:|target_port|
300 * using the SSL protocol. If |proxy| is set it will be used as a HTTP proxy.
301 */
302static BIO *
303make_ssl_bio (SSL_CTX *ctx, const char *target_host, const char *target_port,
304 char *proxy)
305{
306 BIO *bio_socket = NULL;
307 BIO *bio_ssl = NULL;
308 BIO *bio_proxy = NULL;
309 char *scheme = NULL;
310 char *proxy_host = NULL;
311 char *proxy_port = NULL;
312
313 if ( !(bio_ssl = BIO_new_ssl (ctx, 1)))
Will Drewryc45952f2013-09-03 13:51:24 -0500314 die ("BIO_new_ssl failed\n");
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100315
316 if (proxy)
317 {
318 verb ("V: using proxy %s\n", proxy);
319 parse_proxy_uri (proxy, &scheme, &proxy_host, &proxy_port);
320
321 if ( !(bio_proxy = BIO_create_proxy (scheme, target_host, target_port)))
322 die ("BIO_create_proxy failed\n");
323
324 bio_socket = BIO_create_socket (proxy_host, proxy_port);
325
326 BIO_push (bio_ssl, bio_proxy);
327 BIO_push (bio_ssl, bio_socket);
328 }
329 else
330 {
331 bio_socket = BIO_create_socket (target_host, target_port);
332 BIO_push (bio_ssl, bio_socket);
333 }
334 return bio_ssl;
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400335}
Christian Grothoff90e9f832012-02-07 11:16:47 +0100336
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200337/** helper function for 'malloc' */
338static void *
339xmalloc (size_t size)
340{
341 void *ptr;
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200342 if (0 == size)
Will Drewryc45952f2013-09-03 13:51:24 -0500343 die ("xmalloc: zero size\n");
344 ptr = malloc (size);
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200345 if (NULL == ptr)
Will Drewryc45952f2013-09-03 13:51:24 -0500346 die ("xmalloc: out of memory (allocating %zu bytes)\n", size);
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200347 return ptr;
348}
349
350
351/** helper function for 'free' */
352static void
353xfree (void *ptr)
354{
Daniel Borkmann592acc52012-07-30 14:08:02 +0200355 if (NULL == ptr)
Will Drewryc45952f2013-09-03 13:51:24 -0500356 die ("xfree: NULL pointer given as argument\n");
357 free (ptr);
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200358}
359
360
David Benjamin1aba54c2017-02-05 16:24:49 -0500361static int
362verify_with_server_time (X509_STORE_CTX *store_ctx, void *arg)
Jacob Appelbaum9f807292012-07-16 18:24:37 -0700363{
David Benjamin1aba54c2017-02-05 16:24:49 -0500364 SSL *ssl = X509_STORE_CTX_get_ex_data (
365 store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
366
367 // XXX TODO: If we want to trust the remote system for time,
368 // can we just read that time out of the remote system and if the
369 // cert verifies, decide that the time is reasonable?
370 // Such a process seems to indicate that a once valid cert would be
371 // forever valid - we stopgap that by ensuring it isn't less than
372 // the latest compiled_time and isn't above max_reasonable_time...
373 // XXX TODO: Solve eternal question about the Chicken and the Egg...
374 uint32_t compiled_time = RECENT_COMPILE_DATE;
375 uint32_t max_reasonable_time = MAX_REASONABLE_TIME;
376 uint32_t server_time;
377 verb ("V: freezing time for x509 verification\n");
378 SSL_get_server_random(ssl, (unsigned char *)&server_time,
379 sizeof (uint32_t));
380 if (compiled_time < ntohl (server_time)
381 &&
382 ntohl (server_time) < max_reasonable_time)
Jacob Appelbaum9f807292012-07-16 18:24:37 -0700383 {
David Benjamin1aba54c2017-02-05 16:24:49 -0500384 verb ("V: remote peer provided: %d, preferred over compile time: %d\n",
385 ntohl (server_time), compiled_time);
386 verb ("V: freezing time with X509_VERIFY_PARAM_set_time\n");
387 X509_STORE_CTX_set_time (
388 store_ctx, 0, (time_t) ntohl (server_time) + 86400);
Jacob Appelbaum9f807292012-07-16 18:24:37 -0700389 }
David Benjamin1aba54c2017-02-05 16:24:49 -0500390 else
391 {
392 die ("V: the remote server is a false ticker! server: %d compile: %d\n",
393 ntohl (server_time), compiled_time);
394 }
395
396 return X509_verify_cert (store_ctx);
Jacob Appelbaum123bb3e2012-07-16 17:25:34 -0700397}
398
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700399uint32_t
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700400get_certificate_keybits (EVP_PKEY *public_key)
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700401{
402 /*
403 In theory, we could use check_bitlen_dsa() and check_bitlen_rsa()
404 */
405 uint32_t key_bits;
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600406 switch (EVP_PKEY_id (public_key))
Will Drewryc45952f2013-09-03 13:51:24 -0500407 {
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700408 case EVP_PKEY_RSA:
Will Drewryc45952f2013-09-03 13:51:24 -0500409 verb ("V: key type: EVP_PKEY_RSA\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700410 break;
411 case EVP_PKEY_RSA2:
Will Drewryc45952f2013-09-03 13:51:24 -0500412 verb ("V: key type: EVP_PKEY_RSA2\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700413 break;
414 case EVP_PKEY_DSA:
Will Drewryc45952f2013-09-03 13:51:24 -0500415 verb ("V: key type: EVP_PKEY_DSA\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700416 break;
417 case EVP_PKEY_DSA1:
Will Drewryc45952f2013-09-03 13:51:24 -0500418 verb ("V: key type: EVP_PKEY_DSA1\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700419 break;
420 case EVP_PKEY_DSA2:
Will Drewryc45952f2013-09-03 13:51:24 -0500421 verb ("V: key type: EVP_PKEY_DSA2\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700422 break;
423 case EVP_PKEY_DSA3:
Will Drewryc45952f2013-09-03 13:51:24 -0500424 verb ("V: key type: EVP_PKEY_DSA3\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700425 break;
426 case EVP_PKEY_DSA4:
Will Drewryc45952f2013-09-03 13:51:24 -0500427 verb ("V: key type: EVP_PKEY_DSA4\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700428 break;
429 case EVP_PKEY_DH:
Will Drewryc45952f2013-09-03 13:51:24 -0500430 verb ("V: key type: EVP_PKEY_DH\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700431 break;
432 case EVP_PKEY_EC:
Will Drewryc45952f2013-09-03 13:51:24 -0500433 verb ("V: key type: EVP_PKEY_EC\n");
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700434 break;
Will Drewryc45952f2013-09-03 13:51:24 -0500435 // Should we also care about EVP_PKEY_HMAC and EVP_PKEY_CMAC?
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700436 default:
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700437 die ("unknown public key type\n");
438 break;
Will Drewryc45952f2013-09-03 13:51:24 -0500439 }
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600440 key_bits = EVP_PKEY_bits (public_key);
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700441 verb ("V: keybits: %d\n", key_bits);
442 return key_bits;
443}
444
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700445uint32_t
Will Drewryc45952f2013-09-03 13:51:24 -0500446dns_label_count (char *label, char *delim)
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700447{
448 char *label_tmp;
449 char *saveptr;
450 char *saveptr_tmp;
451 uint32_t label_count;
Will Drewryc45952f2013-09-03 13:51:24 -0500452 label_tmp = strdup (label);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700453 label_count = 0;
454 saveptr = NULL;
455 saveptr_tmp = NULL;
Will Drewryc45952f2013-09-03 13:51:24 -0500456 saveptr = strtok_r (label_tmp, delim, &saveptr);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700457 if (NULL != saveptr)
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700458 {
Will Drewryc45952f2013-09-03 13:51:24 -0500459 // Did we find our first label?
460 if (saveptr[0] != delim[0])
461 {
462 label_count++;
463 verb ("V: label found; total label count: %d\n", label_count);
464 }
465 do
466 {
467 // Find all subsequent labels
468 label_count++;
469 saveptr_tmp = strtok_r (NULL, delim, &saveptr);
470 verb ("V: label found; total label count: %d\n", label_count);
471 }
472 while (NULL != saveptr_tmp);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700473 }
Will Drewryc45952f2013-09-03 13:51:24 -0500474 free (label_tmp);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700475 return label_count;
476}
477
478// first we split strings on '.'
479// then we call each split string a 'label'
480// Do not allow '*' for the top level domain label; eg never allow *.*.com
481// Do not allow '*' for subsequent subdomains; eg never allow *.foo.example.com
482// Do allow *.example.com
483uint32_t
484check_wildcard_match_rfc2595 (const char *orig_hostname,
Will Drewryc45952f2013-09-03 13:51:24 -0500485 const char *orig_cert_wild_card)
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700486{
487 char *hostname;
488 char *hostname_to_free;
489 char *cert_wild_card;
490 char *cert_wild_card_to_free;
491 char *expected_label;
492 char *wildcard_label;
493 char *delim;
494 char *wildchar;
495 uint32_t ok;
496 uint32_t wildcard_encountered;
497 uint32_t label_count;
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700498 // First we copy the original strings
Will Drewryc45952f2013-09-03 13:51:24 -0500499 hostname = strndup (orig_hostname, strlen (orig_hostname));
500 cert_wild_card = strndup (orig_cert_wild_card, strlen (orig_cert_wild_card));
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700501 hostname_to_free = hostname;
502 cert_wild_card_to_free = cert_wild_card;
Will Drewryc45952f2013-09-03 13:51:24 -0500503 delim = strdup (".");
504 wildchar = strdup ("*");
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700505 verb ("V: Inspecting '%s' for possible wildcard match against '%s'\n",
Will Drewryc45952f2013-09-03 13:51:24 -0500506 hostname, cert_wild_card);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700507 // By default we have not processed any labels
Will Drewryc45952f2013-09-03 13:51:24 -0500508 label_count = dns_label_count (cert_wild_card, delim);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700509 // By default we have no match
510 ok = 0;
511 wildcard_encountered = 0;
512 // First - do we have labels? If not, we refuse to even try to match
Will Drewryc45952f2013-09-03 13:51:24 -0500513 if ( (NULL != strpbrk (cert_wild_card, delim)) &&
514 (NULL != strpbrk (hostname, delim)) &&
515 (label_count <= ( (uint32_t) RFC2595_MIN_LABEL_COUNT)))
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700516 {
Will Drewryc45952f2013-09-03 13:51:24 -0500517 if (wildchar[0] == cert_wild_card[0])
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700518 {
Will Drewryc45952f2013-09-03 13:51:24 -0500519 verb ("V: Found wildcard in at start of provided certificate name\n");
520 do
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700521 {
Will Drewryc45952f2013-09-03 13:51:24 -0500522 // Skip over the bytes between the first char and until the next label
523 wildcard_label = strsep (&cert_wild_card, delim);
524 expected_label = strsep (&hostname, delim);
525 if (NULL != wildcard_label &&
526 NULL != expected_label &&
527 NULL != hostname &&
528 NULL != cert_wild_card)
529 {
530 // Now we only consider this wildcard valid if the rest of the
531 // hostnames match verbatim
532 verb ("V: Attempting match of '%s' against '%s'\n",
533 expected_label, wildcard_label);
534 // This is the case where we have a label that begins with wildcard
535 // Furthermore, we only allow this for the first label
536 if (wildcard_label[0] == wildchar[0] &&
537 0 == wildcard_encountered && 0 == ok)
538 {
539 verb ("V: Forced match of '%s' against '%s'\n", expected_label, wildcard_label);
540 wildcard_encountered = 1;
541 }
542 else
543 {
544 verb ("V: Attempting match of '%s' against '%s'\n",
545 hostname, cert_wild_card);
546 if (0 == strcasecmp (expected_label, wildcard_label) &&
547 label_count >= ( (uint32_t) RFC2595_MIN_LABEL_COUNT))
548 {
549 ok = 1;
550 verb ("V: remaining labels match!\n");
551 break;
552 }
553 else
554 {
555 ok = 0;
556 verb ("V: remaining labels do not match!\n");
557 break;
558 }
559 }
560 }
561 else
562 {
563 // We hit this case when we have a mismatched number of labels
564 verb ("V: NULL label; no wildcard here\n");
565 break;
566 }
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700567 }
Will Drewryc45952f2013-09-03 13:51:24 -0500568 while (0 != wildcard_encountered && label_count <= RFC2595_MIN_LABEL_COUNT);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700569 }
Will Drewryc45952f2013-09-03 13:51:24 -0500570 else
571 {
572 verb ("V: Not a RFC 2595 wildcard\n");
573 }
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700574 }
Will Drewryc45952f2013-09-03 13:51:24 -0500575 else
576 {
577 verb ("V: Not a valid wildcard certificate\n");
578 ok = 0;
579 }
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700580 // Free our copies
Will Drewryc45952f2013-09-03 13:51:24 -0500581 free (wildchar);
582 free (delim);
583 free (hostname_to_free);
584 free (cert_wild_card_to_free);
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700585 if (wildcard_encountered & ok && label_count >= RFC2595_MIN_LABEL_COUNT)
Will Drewryc45952f2013-09-03 13:51:24 -0500586 {
587 verb ("V: wildcard match of %s against %s\n",
588 orig_hostname, orig_cert_wild_card);
589 return (wildcard_encountered & ok);
590 }
591 else
592 {
593 verb ("V: wildcard match failure of %s against %s\n",
594 orig_hostname, orig_cert_wild_card);
595 return 0;
596 }
Jacob Appelbaumad12a3a2012-08-05 17:47:17 -0700597}
598
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700599/**
600 This extracts the first commonName and checks it against hostname.
601*/
602uint32_t
603check_cn (SSL *ssl, const char *hostname)
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700604{
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200605 int ok = 0;
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700606 uint32_t ret;
607 char *cn_buf;
608 X509 *certificate;
Jacob Appelbaum8f7682d2012-07-31 03:28:26 -0700609 X509_NAME *xname;
Will Drewryc45952f2013-09-03 13:51:24 -0500610 cn_buf = xmalloc (TLSDATE_HOST_NAME_MAX + 1);
611 certificate = SSL_get_peer_certificate (ssl);
Jacob Appelbaum8f7682d2012-07-31 03:28:26 -0700612 if (NULL == certificate)
Will Drewryc45952f2013-09-03 13:51:24 -0500613 {
614 die ("Unable to extract certificate\n");
615 }
616 memset (cn_buf, '\0', (TLSDATE_HOST_NAME_MAX + 1));
617 xname = X509_get_subject_name (certificate);
618 ret = X509_NAME_get_text_by_NID (xname, NID_commonName,
619 cn_buf, TLSDATE_HOST_NAME_MAX);
620 if (-1 == ret && ret != strlen (hostname))
621 {
622 die ("Unable to extract commonName\n");
623 }
624 if (strcasecmp (cn_buf, hostname))
625 {
626 verb ("V: commonName mismatch! Expected: %s - received: %s\n",
627 hostname, cn_buf);
628 }
629 else
630 {
631 verb ("V: commonName matched: %s\n", cn_buf);
632 ok = 1;
633 }
634 X509_NAME_free (xname);
635 X509_free (certificate);
636 xfree (cn_buf);
Daniel Borkmann23c2b802012-07-30 14:06:12 +0200637 return ok;
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700638}
639
640/**
641 Search for a hostname match in the SubjectAlternativeNames.
642*/
643uint32_t
644check_san (SSL *ssl, const char *hostname)
645{
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700646 X509 *cert;
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700647 int extcount, ok = 0;
648 /* What an OpenSSL mess ... */
Will Drewryc45952f2013-09-03 13:51:24 -0500649 if (NULL == (cert = SSL_get_peer_certificate (ssl)))
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700650 {
Will Drewryc45952f2013-09-03 13:51:24 -0500651 die ("Getting certificate failed\n");
652 }
653 if ( (extcount = X509_get_ext_count (cert)) > 0)
654 {
655 int i;
656 for (i = 0; i < extcount; ++i)
Jacob Appelbaum145886f2012-07-30 01:56:53 -0700657 {
Will Drewryc45952f2013-09-03 13:51:24 -0500658 const char *extstr;
659 X509_EXTENSION *ext;
660 ext = X509_get_ext (cert, i);
661 extstr = OBJ_nid2sn (OBJ_obj2nid (X509_EXTENSION_get_object (ext)));
662 if (!strcmp (extstr, "subjectAltName"))
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700663 {
Will Drewryc45952f2013-09-03 13:51:24 -0500664 int j;
665 void *extvalstr;
Will Drewryc45952f2013-09-03 13:51:24 -0500666 STACK_OF (CONF_VALUE) *val;
667 CONF_VALUE *nval;
668#if OPENSSL_VERSION_NUMBER >= 0x10000000L
669 const
670#endif
671 X509V3_EXT_METHOD *method;
672 if (! (method = X509V3_EXT_get (ext)))
673 {
674 break;
675 }
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600676 extvalstr = X509V3_EXT_d2i(ext);
Will Drewryc45952f2013-09-03 13:51:24 -0500677 if (!extvalstr)
678 {
679 break;
680 }
681 if (method->i2v)
682 {
683 val = method->i2v (method, extvalstr, NULL);
684 for (j = 0; j < sk_CONF_VALUE_num (val); ++j)
685 {
686 nval = sk_CONF_VALUE_value (val, j);
687 if ( (!strcasecmp (nval->name, "DNS") &&
688 !strcasecmp (nval->value, hostname)) ||
689 (!strcasecmp (nval->name, "iPAddress") &&
690 !strcasecmp (nval->value, hostname)))
691 {
692 verb ("V: subjectAltName matched: %s, type: %s\n", nval->value, nval->name); // We matched this; so it's safe to print
693 ok = 1;
694 break;
695 }
696 // Attempt to match subjectAltName DNS names
697 if (!strcasecmp (nval->name, "DNS"))
698 {
699 ok = check_wildcard_match_rfc2595 (hostname, nval->value);
700 if (ok)
701 {
702 break;
703 }
704 }
705 verb ("V: subjectAltName found but not matched: %s, type: %s\n", nval->value, nval->name); // XXX: Clean this string!
706 }
707 }
708 }
709 else
710 {
711 verb ("V: found non subjectAltName extension\n");
712 }
713 if (ok)
714 {
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700715 break;
716 }
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700717 }
Jacob Appelbaumfc9761e2012-07-30 01:29:49 -0700718 }
Will Drewryc45952f2013-09-03 13:51:24 -0500719 else
720 {
721 verb ("V: no X509_EXTENSION field(s) found\n");
722 }
723 X509_free (cert);
Jacob Appelbaum145886f2012-07-30 01:56:53 -0700724 return ok;
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700725}
726
727uint32_t
728check_name (SSL *ssl, const char *hostname)
729{
730 uint32_t ret;
Jacob Appelbaum193e5612012-07-30 03:01:35 -0700731 ret = 0;
Will Drewryc45952f2013-09-03 13:51:24 -0500732 ret = check_cn (ssl, hostname);
733 ret += check_san (ssl, hostname);
Jacob Appelbaum193e5612012-07-30 03:01:35 -0700734 if (0 != ret && 0 < ret)
Will Drewryc45952f2013-09-03 13:51:24 -0500735 {
736 verb ("V: hostname verification passed\n");
737 }
738 else
739 {
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100740 die ("hostname verification failed for host %s!\n", g_host);
Will Drewryc45952f2013-09-03 13:51:24 -0500741 }
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700742 return ret;
743}
744
745uint32_t
746verify_signature (SSL *ssl, const char *hostname)
747{
748 long ssl_verify_result;
749 X509 *certificate;
Will Drewryc45952f2013-09-03 13:51:24 -0500750 certificate = SSL_get_peer_certificate (ssl);
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700751 if (NULL == certificate)
Will Drewryc45952f2013-09-03 13:51:24 -0500752 {
753 die ("Getting certificate failed\n");
754 }
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700755 // In theory, we verify that the cert is valid
Will Drewryc45952f2013-09-03 13:51:24 -0500756 ssl_verify_result = SSL_get_verify_result (ssl);
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700757 switch (ssl_verify_result)
Will Drewryc45952f2013-09-03 13:51:24 -0500758 {
759 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
760 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
761 die ("certificate is self signed\n");
762 case X509_V_OK:
763 verb ("V: certificate verification passed\n");
764 break;
765 default:
766 die ("certification verification error: %ld\n",
767 ssl_verify_result);
768 }
769 return 0;
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700770}
771
772void
773check_key_length (SSL *ssl)
774{
775 uint32_t key_bits;
776 X509 *certificate;
777 EVP_PKEY *public_key;
778 certificate = SSL_get_peer_certificate (ssl);
A soldier31056a62012-08-16 01:10:57 -0700779 if (NULL == certificate)
Will Drewryc45952f2013-09-03 13:51:24 -0500780 {
781 die ("Getting certificate failed\n");
782 }
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700783 public_key = X509_get_pubkey (certificate);
784 if (NULL == public_key)
Will Drewryc45952f2013-09-03 13:51:24 -0500785 {
786 die ("public key extraction failure\n");
787 }
788 else
789 {
790 verb ("V: public key is ready for inspection\n");
791 }
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700792 key_bits = get_certificate_keybits (public_key);
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600793 if (MIN_PUB_KEY_LEN >= key_bits && EVP_PKEY_id(public_key) != EVP_PKEY_EC)
Will Drewryc45952f2013-09-03 13:51:24 -0500794 {
795 die ("Unsafe public key size: %d bits\n", key_bits);
796 }
797 else
798 {
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600799 if (EVP_PKEY_id(public_key) == EVP_PKEY_EC)
Will Drewryc45952f2013-09-03 13:51:24 -0500800 if (key_bits >= MIN_ECC_PUB_KEY_LEN
801 && key_bits <= MAX_ECC_PUB_KEY_LEN)
802 {
803 verb ("V: ECC key length appears safe\n");
804 }
805 else
806 {
807 die ("Unsafe ECC key size: %d bits\n", key_bits);
808 }
809 else
810 {
811 verb ("V: key length appears safe\n");
812 }
813 }
Jacob Appelbaum42ccf9d2012-07-29 16:30:15 -0700814 EVP_PKEY_free (public_key);
815}
816
817void
818inspect_key (SSL *ssl, const char *hostname)
819{
Will Drewryc45952f2013-09-03 13:51:24 -0500820 verify_signature (ssl, hostname);
821 check_name (ssl, hostname);
Jacob Appelbaumc4d3f9f2012-07-17 18:20:56 -0700822}
823
Christian Grothoff88b422b2012-02-14 00:35:09 +0100824/**
825 * Run SSL handshake and store the resulting time value in the
826 * 'time_map'.
827 *
828 * @param time_map where to store the current time
829 */
830static void
Jacob Appelbaumc732f4e2012-07-15 22:38:46 -0400831run_ssl (uint32_t *time_map, int time_is_an_illusion)
Christian Grothoff90e9f832012-02-07 11:16:47 +0100832{
Christian Grothoff90e9f832012-02-07 11:16:47 +0100833 BIO *s_bio;
Christian Grothoff90e9f832012-02-07 11:16:47 +0100834 SSL_CTX *ctx;
835 SSL *ssl;
Jacob Appelbaum12e15c92013-01-07 11:17:32 -0800836 struct stat statbuf;
Christian Grothoff90e9f832012-02-07 11:16:47 +0100837 SSL_load_error_strings();
838 SSL_library_init();
Christian Grothoff90e9f832012-02-07 11:16:47 +0100839 ctx = NULL;
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600840
Mattias Nissler3da9da42020-02-13 11:27:41 +0100841 /*
842 * Some of the XYZ_method() calls below are marked deprecated. We still want
843 * to use them for compatibility reasons, so silence the warnings or they'll
844 * break the build.
845 */
846#pragma GCC diagnostic push
847#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
848
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100849 if (0 == strcmp ("sslv23", g_protocol))
Jacob Appelbaum12e15c92013-01-07 11:17:32 -0800850 {
Will Drewryc45952f2013-09-03 13:51:24 -0500851 verb ("V: using SSLv23_client_method()\n");
852 ctx = SSL_CTX_new (SSLv23_client_method());
Jacob Appelbaum12e15c92013-01-07 11:17:32 -0800853 }
Mattias Nissler2ca14572020-01-13 15:01:43 +0100854#ifndef OPENSSL_NO_SSL3_METHOD
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100855 else if (0 == strcmp ("sslv3", g_protocol))
Will Drewryc45952f2013-09-03 13:51:24 -0500856 {
857 verb ("V: using SSLv3_client_method()\n");
858 ctx = SSL_CTX_new (SSLv3_client_method());
859 }
Mattias Nissler2ca14572020-01-13 15:01:43 +0100860#endif
861#ifndef OPENSSL_NO_TLS1_METHOD
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100862 else if (0 == strcmp ("tlsv1", g_protocol))
Will Drewryc45952f2013-09-03 13:51:24 -0500863 {
864 verb ("V: using TLSv1_client_method()\n");
865 ctx = SSL_CTX_new (TLSv1_client_method());
866 }
Mattias Nissler2ca14572020-01-13 15:01:43 +0100867#endif
868#ifndef OPENSSL_NO_TLS1_1_METHOD
869 else if (0 == strcmp ("tlsv11", g_protocol))
870 {
871 verb ("V: using TLSv1_1_client_method()\n");
872 ctx = SSL_CTX_new (TLSv1_1_client_method());
873 }
874#endif
875#ifndef OPENSSL_NO_TLS1_2_METHOD
876 else if (0 == strcmp ("tlsv12", g_protocol))
877 {
878 verb ("V: using TLSv1_2_client_method()\n");
879 ctx = SSL_CTX_new (TLSv1_2_client_method());
880 }
881#endif
Will Drewryc45952f2013-09-03 13:51:24 -0500882 else
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100883 die ("Unsupported protocol `%s'\n", g_protocol);
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600884
Mattias Nissler3da9da42020-02-13 11:27:41 +0100885#pragma GCC diagnostic pop
886
Will Drewryc45952f2013-09-03 13:51:24 -0500887 if (ctx == NULL)
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100888 die ("OpenSSL failed to support protocol `%s'\n", g_protocol);
889 if (g_ca_racket)
Will Drewryc45952f2013-09-03 13:51:24 -0500890 {
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100891 if (-1 == stat (g_ca_cert_container, &statbuf))
Will Drewryc45952f2013-09-03 13:51:24 -0500892 {
893 die ("Unable to stat CA certficate container\n");
894 }
895 else
896 {
897 switch (statbuf.st_mode & S_IFMT)
898 {
899 case S_IFREG:
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100900 if (1 != SSL_CTX_load_verify_locations(
901 ctx, g_ca_cert_container, NULL))
902 fprintf(stderr, "SSL_CTX_load_verify_locations failed\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500903 break;
904 case S_IFDIR:
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100905 if (1 != SSL_CTX_load_verify_locations(
906 ctx, NULL, g_ca_cert_container))
907 fprintf(stderr, "SSL_CTX_load_verify_locations failed\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500908 break;
909 default:
910 die ("Unable to load CA certficate container\n");
911 }
912 }
913 }
David Benjamin1aba54c2017-02-05 16:24:49 -0500914
915 if (time_is_an_illusion)
916 {
917 SSL_CTX_set_cert_verify_callback (ctx, verify_with_server_time, NULL);
918 }
919
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100920 verb ("V: setting up connection to %s:%s\n", g_host, g_port);
921 if (NULL == (s_bio = make_ssl_bio(ctx, g_host, g_port, g_proxy)))
Christian Grothoff90e9f832012-02-07 11:16:47 +0100922 die ("SSL BIO setup failed\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500923 BIO_get_ssl (s_bio, &ssl);
Christian Grothoff90e9f832012-02-07 11:16:47 +0100924 if (NULL == ssl)
925 die ("SSL setup failed\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500926 SSL_set_mode (ssl, SSL_MODE_AUTO_RETRY);
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100927 SSL_set_tlsext_host_name (ssl, g_host);
Will Drewryc45952f2013-09-03 13:51:24 -0500928 if (NULL == BIO_new_fp (stdout, BIO_NOCLOSE))
929 die ("BIO_new_fp returned error, possibly: %s", strerror (errno));
Jacob Appelbaum405a7932012-02-15 15:22:02 -0800930 // This should run in seccomp
931 // eg: prctl(PR_SET_SECCOMP, 1);
Pavol Markod9dd19d2020-01-17 23:32:57 +0100932 verb ("V: BIO_do_connect\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500933 if (1 != BIO_do_connect (s_bio)) // XXX TODO: BIO_should_retry() later?
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800934 die ("SSL connection failed\n");
Pavol Markod9dd19d2020-01-17 23:32:57 +0100935 verb ("V: BIO_do_handshake\n");
Will Drewryc45952f2013-09-03 13:51:24 -0500936 if (1 != BIO_do_handshake (s_bio))
Philipp Winterd96af622012-02-15 22:15:50 +0100937 die ("SSL handshake failed\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100938 // Verify the peer certificate against the CA certs on the local system
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100939 if (g_ca_racket)
Will Drewryc45952f2013-09-03 13:51:24 -0500940 {
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100941 inspect_key (ssl, g_host);
Will Drewryc45952f2013-09-03 13:51:24 -0500942 }
943 else
944 {
945 verb ("V: Certificate verification skipped!\n");
946 }
947 check_key_length (ssl);
Christian Grothoff90e9f832012-02-07 11:16:47 +0100948 // from /usr/include/openssl/ssl3.h
Jacob Appelbaumca64b502012-06-28 21:54:14 -0700949 // ssl->s3->server_random is an unsigned char of 32 bits
Daniel Kurtza2d82d52019-06-07 15:26:54 -0600950 SSL_get_server_random(ssl, (unsigned char *)time_map, sizeof(uint32_t));
Will Drewryc45952f2013-09-03 13:51:24 -0500951 SSL_free (ssl);
952 SSL_CTX_free (ctx);
Christian Grothoff88b422b2012-02-14 00:35:09 +0100953}
Christian Grothoff90e9f832012-02-07 11:16:47 +0100954
Christian Grothoff191cd982012-02-14 00:48:45 +0100955/** drop root rights and become 'nobody' */
Christian Grothoffbd15a222012-02-14 00:40:57 +0100956
Christian Grothoff88b422b2012-02-14 00:35:09 +0100957int
Will Drewryc45952f2013-09-03 13:51:24 -0500958main (int argc, char **argv)
Christian Grothoff88b422b2012-02-14 00:35:09 +0100959{
960 uint32_t *time_map;
David Goulet0809df12012-07-31 23:27:34 -0400961 struct tlsdate_time start_time, end_time, warp_time;
Christian Grothoff88b422b2012-02-14 00:35:09 +0100962 int status;
963 pid_t ssl_child;
Christian Grothoffe267c352012-02-14 01:10:54 +0100964 long long rt_time_ms;
965 uint32_t server_time_s;
Jeroen Massarc27a92a2012-07-13 11:49:05 +0200966 int setclock;
967 int showtime;
Will Drewry72706752013-09-13 15:57:10 -0500968 int showtime_raw;
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400969 int timewarp;
Jacob Appelbaumc732f4e2012-07-15 22:38:46 -0400970 int leap;
Elly Fong-Jones4687c5d2012-10-03 17:34:48 -0400971 if (argc != 12)
Christian Grothoff88b422b2012-02-14 00:35:09 +0100972 return 1;
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100973 g_host = argv[1];
974 g_port = argv[2];
975 validate_port(g_port);
976 g_protocol = argv[3];
977 g_ca_cert_container = argv[6];
978 g_ca_racket = (0 != strcmp ("unchecked", argv[4]));
Christian Grothoff88b422b2012-02-14 00:35:09 +0100979 verbose = (0 != strcmp ("quiet", argv[5]));
Jeroen Massarc27a92a2012-07-13 11:49:05 +0200980 setclock = (0 == strcmp ("setclock", argv[7]));
981 showtime = (0 == strcmp ("showtime", argv[8]));
Will Drewry72706752013-09-13 15:57:10 -0500982 showtime_raw = (0 == strcmp ("showtime=raw", argv[8]));
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400983 timewarp = (0 == strcmp ("timewarp", argv[9]));
Jacob Appelbaumc732f4e2012-07-15 22:38:46 -0400984 leap = (0 == strcmp ("leapaway", argv[10]));
Andreea Costinas6d6944a2019-03-27 15:29:09 +0100985 g_proxy = (0 == strcmp ("none", argv[11]) ? NULL : argv[11]);
Michael Ershov2012e5b2021-03-25 13:18:13 +0100986 srandom(time(NULL));
Will Drewryc45952f2013-09-03 13:51:24 -0500987 clock_init_time (&warp_time, RECENT_COMPILE_DATE, 0);
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400988 if (timewarp)
Will Drewryc45952f2013-09-03 13:51:24 -0500989 {
990 verb ("V: RECENT_COMPILE_DATE is %lu.%06lu\n",
991 (unsigned long) CLOCK_SEC (&warp_time),
992 (unsigned long) CLOCK_USEC (&warp_time));
993 }
Jeroen Massarc27a92a2012-07-13 11:49:05 +0200994 /* We are not going to set the clock, thus no need to stay root */
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400995 if (0 == setclock && 0 == timewarp)
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400996 {
Will Drewryc45952f2013-09-03 13:51:24 -0500997 drop_privs_to (UNPRIV_USER, UNPRIV_GROUP);
Jacob Appelbaum894d5272012-07-15 14:32:39 -0400998 }
Will Drewryc45952f2013-09-03 13:51:24 -0500999 time_map = mmap (NULL, sizeof (uint32_t),
1000 PROT_READ | PROT_WRITE,
1001 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
1002 if (MAP_FAILED == time_map)
1003 {
1004 fprintf (stderr, "mmap failed: %s\n",
1005 strerror (errno));
1006 return 1;
1007 }
1008 /* Get the current time from the system clock. */
1009 if (0 != clock_get_real_time (&start_time))
1010 {
1011 die ("Failed to read current time of day: %s\n", strerror (errno));
1012 }
1013 verb ("V: time is currently %lu.%06lu\n",
1014 (unsigned long) CLOCK_SEC (&start_time),
1015 (unsigned long) CLOCK_NSEC (&start_time));
1016 if ( ( (unsigned long) CLOCK_SEC (&start_time)) < ( (unsigned long) CLOCK_SEC (&warp_time)))
1017 {
1018 verb ("V: local clock time is less than RECENT_COMPILE_DATE\n");
1019 if (timewarp)
1020 {
1021 verb ("V: Attempting to warp local clock into the future\n");
1022 if (0 != clock_set_real_time (&warp_time))
1023 {
1024 die ("setting time failed: %s (Attempted to set clock to %lu.%06lu)\n",
1025 strerror (errno),
1026 (unsigned long) CLOCK_SEC (&warp_time),
1027 (unsigned long) CLOCK_SEC (&warp_time));
1028 }
1029 if (0 != clock_get_real_time (&start_time))
1030 {
1031 die ("Failed to read current time of day: %s\n", strerror (errno));
1032 }
1033 verb ("V: time is currently %lu.%06lu\n",
1034 (unsigned long) CLOCK_SEC (&start_time),
1035 (unsigned long) CLOCK_NSEC (&start_time));
1036 verb ("V: It's just a step to the left...\n");
1037 }
1038 }
1039 else
1040 {
1041 verb ("V: time is greater than RECENT_COMPILE_DATE\n");
1042 }
Christian Grothoff191cd982012-02-14 00:48:45 +01001043 /* initialize to bogus value, just to be on the safe side */
Christian Grothoff88b422b2012-02-14 00:35:09 +01001044 *time_map = 0;
Christian Grothoffe267c352012-02-14 01:10:54 +01001045 /* Run SSL interaction in separate process (and not as 'root') */
Christian Grothoff88b422b2012-02-14 00:35:09 +01001046 ssl_child = fork ();
1047 if (-1 == ssl_child)
1048 die ("fork failed: %s\n", strerror (errno));
1049 if (0 == ssl_child)
Will Drewryc45952f2013-09-03 13:51:24 -05001050 {
1051 drop_privs_to (UNPRIV_USER, UNPRIV_GROUP);
1052 run_ssl (time_map, leap);
1053 (void) munmap (time_map, sizeof (uint32_t));
1054 _exit (0);
1055 }
Christian Grothoff88b422b2012-02-14 00:35:09 +01001056 if (ssl_child != waitpid (ssl_child, &status, 0))
1057 die ("waitpid failed: %s\n", strerror (errno));
Will Drewryc45952f2013-09-03 13:51:24 -05001058 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status))))
Christian Grothoff191cd982012-02-14 00:48:45 +01001059 die ("child process failed in SSL handshake\n");
Will Drewryc45952f2013-09-03 13:51:24 -05001060 if (0 != clock_get_real_time (&end_time))
Christian Grothoff88b422b2012-02-14 00:35:09 +01001061 die ("Failed to read current time of day: %s\n", strerror (errno));
Christian Grothoffe267c352012-02-14 01:10:54 +01001062 /* calculate RTT */
Will Drewryc45952f2013-09-03 13:51:24 -05001063 rt_time_ms = (CLOCK_SEC (&end_time) - CLOCK_SEC (&start_time)) * 1000 + (CLOCK_USEC (&end_time) - CLOCK_USEC (&start_time)) / 1000;
Christian Grothoffe267c352012-02-14 01:10:54 +01001064 if (rt_time_ms < 0)
1065 rt_time_ms = 0; /* non-linear time... */
1066 server_time_s = ntohl (*time_map);
1067 munmap (time_map, sizeof (uint32_t));
Jacob Appelbaum9a159572012-02-20 12:39:44 -08001068 verb ("V: server time %u (difference is about %d s) was fetched in %lld ms\n",
Will Drewryc45952f2013-09-03 13:51:24 -05001069 (unsigned int) server_time_s,
1070 CLOCK_SEC (&start_time) - server_time_s,
1071 rt_time_ms);
Philipp Winterb3ca5772012-02-16 11:56:11 +01001072 /* warning if the handshake took too long */
Will Drewryc45952f2013-09-03 13:51:24 -05001073 if (rt_time_ms > TLS_RTT_THRESHOLD)
1074 {
1075 verb ("V: the TLS handshake took more than %d msecs - consider using a different " \
1076 "server or run it again\n", TLS_RTT_THRESHOLD);
1077 }
Will Drewry72706752013-09-13 15:57:10 -05001078 if (showtime_raw)
Will Drewryc45952f2013-09-03 13:51:24 -05001079 {
1080 fwrite (&server_time_s, sizeof (server_time_s), 1, stdout);
1081 }
Jeroen Massarc27a92a2012-07-13 11:49:05 +02001082 if (showtime)
Will Drewryc45952f2013-09-03 13:51:24 -05001083 {
1084 struct tm ltm;
1085 time_t tim = server_time_s;
1086 char buf[256];
1087 localtime_r (&tim, &ltm);
1088 if (0 == strftime (buf, sizeof buf, "%a %b %e %H:%M:%S %Z %Y", &ltm))
1089 {
1090 die ("strftime returned 0\n");
1091 }
1092 fprintf (stdout, "%s\n", buf);
1093 }
Christian Grothoff191cd982012-02-14 00:48:45 +01001094 /* finally, actually set the time */
Jeroen Massarc27a92a2012-07-13 11:49:05 +02001095 if (setclock)
Will Drewryc45952f2013-09-03 13:51:24 -05001096 {
1097 struct tlsdate_time server_time;
1098 clock_init_time (&server_time, server_time_s + (rt_time_ms / 2 / 1000),
1099 (rt_time_ms / 2) % 1000);
1100 // We should never receive a time that is before the time we were last
1101 // compiled; we subscribe to the linear theory of time for this program
1102 // and this program alone!
1103 if (CLOCK_SEC (&server_time) >= MAX_REASONABLE_TIME)
1104 die ("remote server is a false ticker from the future!\n");
1105 if (CLOCK_SEC (&server_time) <= RECENT_COMPILE_DATE)
1106 die ("remote server is a false ticker!\n");
1107 if (0 != clock_set_real_time (&server_time))
1108 die ("setting time failed: %s (Difference from server is about %d)\n",
1109 strerror (errno),
1110 CLOCK_SEC (&start_time) - server_time_s);
1111 verb ("V: setting time succeeded\n");
1112 }
Christian Grothoff90e9f832012-02-07 11:16:47 +01001113 return 0;
1114}