blob: a547be5fc18dfb395c8f6599e9f805d8c7e1e8fd [file] [log] [blame]
Christian Grothoff90e9f832012-02-07 11:16:47 +01001/* Copyright (c) 2012, Jacob Appelbaum.
2 * Copyright (c) 2012, The Tor Project, Inc. */
3/* See LICENSE for licensing information */
4/*
5 This file contains the license for tlsdate,
6 a free software project to set your system clock securely.
7
8 It also lists the licenses for other components used by tlsdate.
9
10 For more information about tlsdate, see https://github.com/ioerror/tlsdate
11
12 If you got this file as a part of a larger bundle,
13 there may be other license terms that you should be aware of.
14
15===============================================================================
16tlsdate is distributed under this license:
17
18Copyright (c) 2011-2012, Jacob Appelbaum <jacob@appelbaum.net>
19Copyright (c) 2011-2012, The Tor Project, Inc.
20
21Redistribution and use in source and binary forms, with or without
22modification, are permitted provided that the following conditions are
23met:
24
25 * Redistributions of source code must retain the above copyright
26notice, this list of conditions and the following disclaimer.
27
28 * Redistributions in binary form must reproduce the above
29copyright notice, this list of conditions and the following disclaimer
30in the documentation and/or other materials provided with the
31distribution.
32
33 * Neither the names of the copyright owners nor the names of its
34contributors may be used to endorse or promote products derived from
35this software without specific prior written permission.
36
37THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48===============================================================================
49If you got tlsdate as a static binary with OpenSSL included, then you should
50know:
51
52 "This product includes software developed by the OpenSSL Project for use in
53 the OpenSSL Toolkit (http://www.openssl.org/)"
54
55===============================================================================
56*/
57
58/**
Christian Grothoffe267c352012-02-14 01:10:54 +010059 * \file tlsdate-helper.c
60 * \brief Helper program that does the actual work of setting the system clock.
Christian Grothoff90e9f832012-02-07 11:16:47 +010061 **/
62
63/*
64 * tlsdate is a tool for setting the system clock by hand or by communication
65 * with the network. It does not set the RTC. It is designed to be as secure as
66 * TLS (RFC 2246) but of course the security of TLS is often reduced to
67 * whichever CA racket you believe is trustworthy. By default, tlsdate trusts
68 * your local CA root store - so any of these companies could assist in a MITM
69 * attack against you and you'd be screwed.
70
71 * This tool is designed to be run by hand or as a system daemon. It must be
72 * run as root or otherwise have the proper caps; it will not be able to set
73 * the system time without running as root or another privileged user.
74 */
75
76#include "tlsdate-config.h"
77
78#include <stdarg.h>
79#include <stdint.h>
Christian Grothoff191cd982012-02-14 00:48:45 +010080#include <stdio.h>
81#include <unistd.h>
Christian Grothoff90e9f832012-02-07 11:16:47 +010082#include <sys/time.h>
Christian Grothoff90e9f832012-02-07 11:16:47 +010083#include <sys/types.h>
84#include <sys/wait.h>
Christian Grothoff88b422b2012-02-14 00:35:09 +010085#include <sys/mman.h>
Christian Grothoff191cd982012-02-14 00:48:45 +010086#include <time.h>
Christian Grothoff90e9f832012-02-07 11:16:47 +010087#include <pwd.h>
Jacob Appelbaum2c385d22012-02-21 14:04:15 -080088#include <grp.h>
Christian Grothoff90e9f832012-02-07 11:16:47 +010089#include <arpa/inet.h>
90
91#include <openssl/bio.h>
92#include <openssl/ssl.h>
93#include <openssl/err.h>
94#include <openssl/evp.h>
95
Christian Grothoff191cd982012-02-14 00:48:45 +010096/** Name of user that we feel safe to run SSL handshake with. */
Christian Grothoff90e9f832012-02-07 11:16:47 +010097#define UNPRIV_USER "nobody"
Jacob Appelbaum2c385d22012-02-21 14:04:15 -080098#define UNPRIV_GROUP "nogroup"
Christian Grothoff90e9f832012-02-07 11:16:47 +010099
100// We should never accept a time before we were compiled
101// We measure in seconds since the epoch - eg: echo `date '+%s'`
102// We set this manually to ensure others can reproduce a build;
103// automation of this will make every build different!
Christian Grothofff5098b42012-02-07 12:20:33 +0100104#define RECENT_COMPILE_DATE (uint32_t) 1328610583
dave bl30d82102012-02-08 01:39:24 +1100105#define MAX_REASONABLE_TIME (uint32_t) 1999991337
Christian Grothoff90e9f832012-02-07 11:16:47 +0100106
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800107// After the duration of the TLS handshake exceeds this threshold
Philipp Winterb3ca5772012-02-16 11:56:11 +0100108// (in msec), a warning is printed.
109#define TLS_RTT_THRESHOLD 2000
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800110
Christian Grothoff90e9f832012-02-07 11:16:47 +0100111static int verbose;
Christian Grothoff90e9f832012-02-07 11:16:47 +0100112
Christian Grothoff88b422b2012-02-14 00:35:09 +0100113static int ca_racket;
114
115static const char *host;
116
117static const char *port;
118
119static const char *protocol;
120
Christian Grothoff191cd982012-02-14 00:48:45 +0100121/** helper function to print message and die */
Christian Grothoff90e9f832012-02-07 11:16:47 +0100122static void
123die(const char *fmt, ...)
124{
125 va_list ap;
126
127 va_start(ap, fmt);
128 vfprintf(stderr, fmt, ap);
129 va_end(ap);
130 exit(1);
131}
132
133
Christian Grothoff90e9f832012-02-07 11:16:47 +0100134/** helper function for 'verbose' output */
135static void
136verb (const char *fmt, ...)
137{
138 va_list ap;
139
Christian Grothoffafbae372012-02-07 14:17:53 +0100140 if (! verbose) return;
Christian Grothoff90e9f832012-02-07 11:16:47 +0100141 va_start(ap, fmt);
142 // FIXME: stdout or stderr for verbose messages?
143 vfprintf(stderr, fmt, ap);
144 va_end(ap);
Christian Grothoff90e9f832012-02-07 11:16:47 +0100145}
146
147
Christian Grothoff88b422b2012-02-14 00:35:09 +0100148/**
149 * Run SSL handshake and store the resulting time value in the
150 * 'time_map'.
151 *
152 * @param time_map where to store the current time
153 */
154static void
155run_ssl (uint32_t *time_map)
Christian Grothoff90e9f832012-02-07 11:16:47 +0100156{
Christian Grothoff90e9f832012-02-07 11:16:47 +0100157 BIO *s_bio;
158 BIO *c_bio;
159 SSL_CTX *ctx;
160 SSL *ssl;
161
Christian Grothoff90e9f832012-02-07 11:16:47 +0100162 SSL_load_error_strings();
163 SSL_library_init();
164
165 ctx = NULL;
166 if (0 == strcmp("sslv23", protocol))
167 {
168 verb ("V: using SSLv23_client_method()\n");
169 ctx = SSL_CTX_new(SSLv23_client_method());
170 } else if (0 == strcmp("sslv3", protocol))
171 {
172 verb ("V: using SSLv3_client_method()\n");
173 ctx = SSL_CTX_new(SSLv3_client_method());
174 } else if (0 == strcmp("tlsv1", protocol))
175 {
176 verb ("V: using TLSv1_client_method()\n");
177 ctx = SSL_CTX_new(TLSv1_client_method());
178 } else
179 die("Unsupported protocol `%s'\n", protocol);
180
181 if (ctx == NULL)
182 die("OpenSSL failed to support protocol `%s'\n", protocol);
183
184 if (ca_racket)
185 {
186 // For google specifically:
187 // SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/Equifax_Secure_CA.pem", NULL);
Jacob Appelbaum43051142012-02-07 14:00:39 +0100188 if (1 != SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs/"))
Christian Grothoff90e9f832012-02-07 11:16:47 +0100189 fprintf(stderr, "SSL_CTX_load_verify_locations failed\n");
190 }
191
192 if (NULL == (s_bio = BIO_new_ssl_connect(ctx)))
193 die ("SSL BIO setup failed\n");
194 BIO_get_ssl(s_bio, &ssl);
195 if (NULL == ssl)
196 die ("SSL setup failed\n");
197 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
198 if ( (1 != BIO_set_conn_hostname(s_bio, host)) ||
199 (1 != BIO_set_conn_port(s_bio, port)) )
200 die ("Failed to initialize connection to `%s:%s'\n", host, port);
201
202 if (NULL == (c_bio = BIO_new_fp(stdout, BIO_NOCLOSE)))
203 die ("FIXME: error message");
204
Jacob Appelbaum405a7932012-02-15 15:22:02 -0800205 // This should run in seccomp
206 // eg: prctl(PR_SET_SECCOMP, 1);
207 if (1 != BIO_do_connect(s_bio)) // XXX TODO: BIO_should_retry() later?
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800208 die ("SSL connection failed\n");
Philipp Winterd96af622012-02-15 22:15:50 +0100209 if (1 != BIO_do_handshake(s_bio))
210 die ("SSL handshake failed\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100211 // Verify the peer certificate against the CA certs on the local system
212 if (ca_racket) {
213 X509 *x509;
214 long ssl_verify_result;
215
Christian Grothoff90e9f832012-02-07 11:16:47 +0100216 if (NULL == (x509 = SSL_get_peer_certificate(ssl)) )
217 die ("Getting SSL certificate failed\n");
218
219 // In theory, we verify that the cert is valid
220 ssl_verify_result = SSL_get_verify_result(ssl);
221 switch (ssl_verify_result)
222 {
223 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
224 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
Christian Grothoffe267c352012-02-14 01:10:54 +0100225 die ("SSL certificate is self signed\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100226 case X509_V_OK:
Christian Grothoffe267c352012-02-14 01:10:54 +0100227 verb ("V: SSL certificate verification passed\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100228 break;
229 default:
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800230 die ("SSL certification verification error: %ld\n",
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800231 ssl_verify_result);
Christian Grothoff90e9f832012-02-07 11:16:47 +0100232 }
Christian Grothoff90e9f832012-02-07 11:16:47 +0100233 } else {
234 verb ("V: Certificate verification skipped!\n");
235 }
236
Christian Grothoff90e9f832012-02-07 11:16:47 +0100237 // from /usr/include/openssl/ssl3.h
238 // ssl->s3->server_random is an unsigned char of 32 bytes
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800239 memcpy(time_map, ssl->s3->server_random, sizeof (uint32_t));
Christian Grothoff88b422b2012-02-14 00:35:09 +0100240}
Christian Grothoff90e9f832012-02-07 11:16:47 +0100241
242
Christian Grothoff191cd982012-02-14 00:48:45 +0100243/** drop root rights and become 'nobody' */
Christian Grothoffbd15a222012-02-14 00:40:57 +0100244static void
245become_nobody ()
246{
247 uid_t uid;
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800248 gid_t gid;
Christian Grothoffbd15a222012-02-14 00:40:57 +0100249 struct passwd *pw;
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800250 struct group *gr;
Christian Grothoffbd15a222012-02-14 00:40:57 +0100251
Christian Grothoffe267c352012-02-14 01:10:54 +0100252 if (0 != getuid ())
253 return; /* not running as root to begin with; should (!) be harmless to continue
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800254 without dropping to 'nobody' (setting time will fail in the end) */
Christian Grothoffbd15a222012-02-14 00:40:57 +0100255 pw = getpwnam(UNPRIV_USER);
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800256 gr = getgrnam(UNPRIV_GROUP);
Christian Grothoffbd15a222012-02-14 00:40:57 +0100257 if (NULL == pw)
258 die ("Failed to obtain UID for `%s'\n", UNPRIV_USER);
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800259 if (NULL == gr)
260 die ("Failed to obtain GID for `%s'\n", UNPRIV_GROUP);
Christian Grothoffbd15a222012-02-14 00:40:57 +0100261 uid = pw->pw_uid;
262 if (0 == uid)
263 die ("UID for `%s' is 0, refusing to run SSL\n", UNPRIV_USER);
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800264 gid = pw->pw_gid;
265 if (0 == gid || 0 == gr->gr_gid)
266 die ("GID for `%s' is 0, refusing to run SSL\n", UNPRIV_USER);
267 if (pw->pw_gid != gr->gr_gid)
Jacob Appelbaumad873f32012-02-21 14:36:10 -0800268 die ("GID for `%s' is not `%s' as expected, refusing to run SSL\n",
269 UNPRIV_USER, UNPRIV_GROUP);
270
271 if (0 != initgroups((const char *)UNPRIV_USER, gr->gr_gid))
272 die ("Unable to initgroups for `%s' in group `%s' as expected\n",
273 UNPRIV_USER, UNPRIV_GROUP);
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800274
275#ifdef HAVE_SETRESGID
276 if (0 != setresgid (gid, gid, gid))
277 die ("Failed to setresgid: %s\n", strerror (errno));
278#else
279 if (0 != (setgid (gid) | setegid (gid)))
280 die ("Failed to setgid: %s\n", strerror (errno));
281#endif
Christian Grothoffbd15a222012-02-14 00:40:57 +0100282#ifdef HAVE_SETRESUID
283 if (0 != setresuid (uid, uid, uid))
284 die ("Failed to setresuid: %s\n", strerror (errno));
285#else
286 if (0 != (setuid (uid) | seteuid (uid)))
287 die ("Failed to setuid: %s\n", strerror (errno));
Jacob Appelbaum2c385d22012-02-21 14:04:15 -0800288#endif
Christian Grothoffbd15a222012-02-14 00:40:57 +0100289}
290
291
Christian Grothoff88b422b2012-02-14 00:35:09 +0100292int
293main(int argc, char **argv)
294{
295 uint32_t *time_map;
296 struct timeval start_timeval;
297 struct timeval end_timeval;
298 int status;
299 pid_t ssl_child;
Christian Grothoffe267c352012-02-14 01:10:54 +0100300 long long rt_time_ms;
301 uint32_t server_time_s;
Christian Grothoff88b422b2012-02-14 00:35:09 +0100302
303 if (argc != 6)
304 return 1;
305 host = argv[1];
306 port = argv[2];
307 protocol = argv[3];
308 ca_racket = (0 != strcmp ("unchecked", argv[4]));
309 verbose = (0 != strcmp ("quiet", argv[5]));
310
311 time_map = mmap (NULL, sizeof (uint32_t),
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800312 PROT_READ | PROT_WRITE,
313 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Christian Grothoff88b422b2012-02-14 00:35:09 +0100314 if (MAP_FAILED == time_map)
315 {
316 fprintf (stderr, "mmap failed: %s\n",
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800317 strerror (errno));
Christian Grothoff88b422b2012-02-14 00:35:09 +0100318 return 1;
319 }
Christian Grothoff191cd982012-02-14 00:48:45 +0100320
321 /* Get the current time from the system clock. */
Christian Grothoff88b422b2012-02-14 00:35:09 +0100322 if (0 != gettimeofday(&start_timeval, NULL))
323 die ("Failed to read current time of day: %s\n", strerror (errno));
Christian Grothoff191cd982012-02-14 00:48:45 +0100324 verb ("V: time is currently %lu.%06lu\n",
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800325 (unsigned long)start_timeval.tv_sec,
326 (unsigned long)start_timeval.tv_usec);
Christian Grothoff191cd982012-02-14 00:48:45 +0100327
328 /* initialize to bogus value, just to be on the safe side */
Christian Grothoff88b422b2012-02-14 00:35:09 +0100329 *time_map = 0;
Christian Grothoffe267c352012-02-14 01:10:54 +0100330
331 /* Run SSL interaction in separate process (and not as 'root') */
Christian Grothoff88b422b2012-02-14 00:35:09 +0100332 ssl_child = fork ();
333 if (-1 == ssl_child)
334 die ("fork failed: %s\n", strerror (errno));
335 if (0 == ssl_child)
336 {
Christian Grothoffbd15a222012-02-14 00:40:57 +0100337 become_nobody ();
Christian Grothoff88b422b2012-02-14 00:35:09 +0100338 run_ssl (time_map);
339 (void) munmap (time_map, sizeof (uint32_t));
340 _exit (0);
341 }
342 if (ssl_child != waitpid (ssl_child, &status, 0))
343 die ("waitpid failed: %s\n", strerror (errno));
Christian Grothoff191cd982012-02-14 00:48:45 +0100344 if (! (WIFEXITED (status) && (0 == WEXITSTATUS (status)) ))
345 die ("child process failed in SSL handshake\n");
Christian Grothoff88b422b2012-02-14 00:35:09 +0100346
347 if (0 != gettimeofday(&end_timeval, NULL))
348 die ("Failed to read current time of day: %s\n", strerror (errno));
Christian Grothoffe267c352012-02-14 01:10:54 +0100349
350 /* calculate RTT */
351 rt_time_ms = (end_timeval.tv_sec - start_timeval.tv_sec) * 1000 + (end_timeval.tv_usec - start_timeval.tv_usec) / 1000;
352 if (rt_time_ms < 0)
353 rt_time_ms = 0; /* non-linear time... */
354 server_time_s = ntohl (*time_map);
355 munmap (time_map, sizeof (uint32_t));
Christian Grothoff88b422b2012-02-14 00:35:09 +0100356
Jacob Appelbaum9a159572012-02-20 12:39:44 -0800357 verb ("V: server time %u (difference is about %d s) was fetched in %lld ms\n",
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800358 (unsigned int) server_time_s,
359 start_timeval.tv_sec - server_time_s,
360 rt_time_ms);
Christian Grothoff191cd982012-02-14 00:48:45 +0100361
Philipp Winterb3ca5772012-02-16 11:56:11 +0100362 /* warning if the handshake took too long */
363 if (rt_time_ms > TLS_RTT_THRESHOLD) {
364 verb ("V: the TLS handshake took more than %d msecs - consider using a different " \
365 "server or run it again\n", TLS_RTT_THRESHOLD);
366 }
367
Christian Grothoff191cd982012-02-14 00:48:45 +0100368 /* finally, actually set the time */
Christian Grothoff90e9f832012-02-07 11:16:47 +0100369 {
370 struct timeval server_time;
Christian Grothoff90e9f832012-02-07 11:16:47 +0100371
Christian Grothoffe267c352012-02-14 01:10:54 +0100372 /* correct server time by half of RTT */
373 server_time.tv_sec = server_time_s + (rt_time_ms / 2 / 1000);
374 server_time.tv_usec = (rt_time_ms / 2) % 1000;
375
Christian Grothoff90e9f832012-02-07 11:16:47 +0100376 // We should never receive a time that is before the time we were last
377 // compiled; we subscribe to the linear theory of time for this program
378 // and this program alone!
Christian Grothofff5098b42012-02-07 12:20:33 +0100379 if (server_time.tv_sec >= MAX_REASONABLE_TIME)
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800380 die("remote server is a false ticker from the future!\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100381 if (server_time.tv_sec <= RECENT_COMPILE_DATE)
Jacob Appelbaumd84456c2012-02-15 16:02:31 -0800382 die ("remote server is a false ticker!\n");
Christian Grothoff90e9f832012-02-07 11:16:47 +0100383 if (0 != settimeofday(&server_time, NULL))
Christian Grothoffe267c352012-02-14 01:10:54 +0100384 die ("setting time failed: %s\n", strerror (errno));
Christian Grothoff90e9f832012-02-07 11:16:47 +0100385 }
386 verb ("V: setting time succeeded\n");
387 return 0;
388}
389