commit 16ee83468552bee9205d6de6b3c4633b160986d6
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Wed Sep 18 14:26:05 2013 -0400
Rudimentary support for HTTP Date headers
Since I'm going on a personal crusade to kill off gmt_unix_time, I
should provide an alternative. That alternative can be the Date
header from HTTP -- unlike gmt_unix_time, the Date header is required
by the RFC to actually be an accurate clock-like clock, and nobody is
trying to get rid of it.
This code is pretty hack-ish and does some nonportable stuff, like
using memmem() and timegm(). It's not super-tolerant of
non-standards-compliant HTTP servers. I hope I didn't make any
pointer mistakes.
---
src/tlsdate-helper.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++--
src/tlsdate-helper.h | 2 +-
src/tlsdate.c | 10 ++-
src/util.c | 6 ++
4 files changed, 183 insertions(+), 10 deletions(-)
diff --git a/src/tlsdate-helper.c b/src/tlsdate-helper.c
index 4058f5e..2a92195 100644
--- a/src/tlsdate-helper.c
+++ b/src/tlsdate-helper.c
@@ -193,6 +193,129 @@ make_ssl_bio(SSL_CTX *ctx)
return ssl;
}
+
+static int
+write_all_to_bio(BIO *bio, const char *string)
+{
+ int n = (int) strlen(string);
+ int r;
+
+ while (n) {
+ r = BIO_write(bio, string, n);
+ if (r > 0) {
+ if (r > n)
+ return -1;
+ n -= r;
+ string += r;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+handle_date_line(const char *dateline, uint32_t *result)
+{
+ int year,mon,day,hour,min,sec;
+ char month[4];
+ struct tm tm;
+ int i;
+ time_t t;
+
+ static const char *MONTHS[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
+
+ if (strncmp("\r\nDate: ", dateline, 8))
+ return 0;
+
+ dateline += 8;
+ verb("V: The alleged date is <%s>\n", dateline);
+
+ while (*dateline == ' ')
+ ++dateline;
+ while (*dateline && *dateline != ' ')
+ ++dateline;
+ while (*dateline == ' ')
+ ++dateline;
+ /* We just skipped over the day of the week. Now we have:*/
+ if (sscanf(dateline, "%d %3s %d %d:%d:%d",
+ &day, month, &year, &hour, &min, &sec) == 6 ||
+ sscanf(dateline, "%d-%3s-%d %d:%d:%d",
+ &day, month, &year, &hour, &min, &sec) == 6 ||
+ sscanf(dateline, "%3s %d %d:%d:%d %d",
+ month, &day, &hour, &min, &sec, &year) == 6) {
+
+ if (year < 100)
+ year += 1900;
+
+ verb("V: Parsed the date: %04d-%s-%02d %02d:%02d:%02d\n",
+ year, month, day, hour, min, sec);
+ } else {
+ verb("V: Couldn't parse date.\n");
+ return -1;
+ }
+
+ for (i = 0; ; ++i) {
+ if (!MONTHS[i])
+ return -2;
+ if (!strcmp(month, MONTHS[i])) {
+ mon = i;
+ break;
+ }
+ }
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = mon;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+
+ t = timegm(&tm);
+ if (t > 0xffffffff || t < 0)
+ return -1;
+
+ *result = (uint32_t) t;
+
+ return 1;
+}
+
+#define MAX_HTTP_HEADERS_SIZE 8192
+
+static int
+read_http_date_from_bio(BIO *bio, uint32_t *result)
+{
+ int n;
+ char buf[MAX_HTTP_HEADERS_SIZE];
+ int buf_len=0;
+ char *dateline, *endofline;
+
+ while (buf_len < sizeof(buf)-1) {
+ n = BIO_read(bio, buf+buf_len, sizeof(buf)-buf_len-1);
+ if (n <= 0)
+ return 0;
+ buf_len += n;
+ buf[buf_len] = 0;
+ verb("V: read %d bytes.", n, buf);
+
+ dateline = memmem(buf, buf_len, "\r\nDate: ", 8);
+ if (NULL == dateline)
+ continue;
+
+ endofline = memmem(dateline+2, buf_len - (dateline-buf+2), "\r\n", 2);
+ if (NULL == endofline)
+ continue;
+
+ *endofline = 0;
+ return handle_date_line(dateline, result);
+ }
+ return -2;
+}
+
/** helper function for 'malloc' */
static void *
xmalloc (size_t size)
@@ -773,6 +896,13 @@ inspect_key (SSL *ssl, const char *hostname)
}
#endif
+#define HTTP_REQUEST \
+ "HEAD / HTTP/1.1\r\n" \
+ "User-Agent: %s\r\n" \
+ "Host: %s\r\n" \
+ "\r\n"
+#define HTTP_USER_AGENT "TLSDate/1.0"
+
#ifdef USE_POLARSSL
void
check_timestamp (uint32_t server_time)
@@ -819,9 +949,12 @@ static int ssl_do_handshake_part(ssl_context *ssl)
* 'time_map'.
*
* @param time_map where to store the current time
+ * @param time_is_an_illusion
+ * @param http whether to do an http request and take the date from that
+ * instead.
*/
static void
-run_ssl (uint32_t *time_map, int time_is_an_illusion)
+run_ssl (uint32_t *time_map, int time_is_an_illusion, int http)
{
entropy_context entropy;
ctr_drbg_context ctr_drbg;
@@ -958,14 +1091,18 @@ run_ssl (uint32_t *time_map, int time_is_an_illusion)
* 'time_map'.
*
* @param time_map where to store the current time
+ * @param time_is_an_illusion
+ * @param http whether to do an http request and take the date from that
+ * instead.
*/
static void
-run_ssl (uint32_t *time_map, int time_is_an_illusion)
+run_ssl (uint32_t *time_map, int time_is_an_illusion, int http)
{
BIO *s_bio;
SSL_CTX *ctx;
SSL *ssl;
struct stat statbuf;
+ uint32_t result_time;
SSL_load_error_strings();
SSL_library_init();
@@ -1044,6 +1181,28 @@ run_ssl (uint32_t *time_map, int time_is_an_illusion)
if (1 != BIO_do_handshake(s_bio))
die ("SSL handshake failed\n");
+ if (http) {
+ char buf[1024];
+ verb("V: Starting HTTP\n");
+ if (snprintf(buf, sizeof(buf),
+ HTTP_REQUEST, HTTP_USER_AGENT, hostname_to_verify) >= 1024)
+ die("hostname too long");
+ buf[1023]='\0'; /* Unneeded. */
+ verb("V: Writing HTTP request\n");
+ if (1 != write_all_to_bio(s_bio, buf))
+ die ("write all to bio failed.\n");
+ verb("V: Reading HTTP response\n");
+ if (1 != read_http_date_from_bio(s_bio, &result_time))
+ die ("read all from bio failed.\n");
+ verb("V: Got HTTP response. T=%lu\n", (unsigned long)result_time);
+
+ result_time = htonl(result_time);
+ } else {
+ // from /usr/include/openssl/ssl3.h
+ // ssl->s3->server_random is an unsigned char of 32 bits
+ memcpy(&result_time, ssl->s3->server_random, sizeof (uint32_t));
+ }
+
// Verify the peer certificate against the CA certs on the local system
if (ca_racket) {
inspect_key (ssl, hostname_to_verify);
@@ -1051,9 +1210,9 @@ run_ssl (uint32_t *time_map, int time_is_an_illusion)
verb ("V: Certificate verification skipped!\n");
}
check_key_length(ssl);
- // from /usr/include/openssl/ssl3.h
- // ssl->s3->server_random is an unsigned char of 32 bits
- memcpy(time_map, ssl->s3->server_random, sizeof (uint32_t));
+
+ memcpy(time_map, &result_time, sizeof (uint32_t));
+
SSL_free(ssl);
SSL_CTX_free(ctx);
}
@@ -1074,8 +1233,9 @@ main(int argc, char **argv)
int showtime_raw;
int timewarp;
int leap;
+ int http;
- if (argc != 12)
+ if (argc != 13)
return 1;
host = argv[1];
hostname_to_verify = argv[1];
@@ -1090,6 +1250,7 @@ main(int argc, char **argv)
timewarp = (0 == strcmp ("timewarp", argv[9]));
leap = (0 == strcmp ("leapaway", argv[10]));
proxy = (0 == strcmp ("none", argv[11]) ? NULL : argv[11]);
+ http = (0 == (strcmp("http", argv[12])));
/* Initalize warp_time with RECENT_COMPILE_DATE */
clock_init_time(&warp_time, RECENT_COMPILE_DATE, 0);
@@ -1169,7 +1330,7 @@ main(int argc, char **argv)
if (0 == ssl_child)
{
drop_privs_to (UNPRIV_USER, UNPRIV_GROUP);
- run_ssl (time_map, leap);
+ run_ssl (time_map, leap, http);
(void) munmap (time_map, sizeof (uint32_t));
_exit (0);
}
diff --git a/src/tlsdate-helper.h b/src/tlsdate-helper.h
index 2456efc..6890a88 100644
--- a/src/tlsdate-helper.h
+++ b/src/tlsdate-helper.h
@@ -118,6 +118,6 @@ void inspect_key (SSL *ssl, const char *hostname);
uint32_t dns_label_count (char *label, char *delim);
uint32_t check_wildcard_match_rfc2595 (const char *orig_hostname,
const char *orig_cert_wild_card);
-static void run_ssl (uint32_t *time_map, int time_is_an_illusion);
+static void run_ssl (uint32_t *time_map, int time_is_an_illusion, int http);
#endif
diff --git a/src/tlsdate.c b/src/tlsdate.c
index 26e9432..82dd217 100644
--- a/src/tlsdate.c
+++ b/src/tlsdate.c
@@ -94,7 +94,8 @@ usage(void)
" [-V|--showtime] [human|raw]\n"
" [-t|--timewarp]\n"
" [-l|--leap]\n"
- " [-x|--proxy] [url]\n");
+ " [-x|--proxy] [url]\n"
+ " [-w|--http]\n");
}
@@ -112,6 +113,7 @@ main(int argc, char **argv)
int timewarp;
int leap;
const char *proxy;
+ int http;
host = DEFAULT_HOST;
port = DEFAULT_PORT;
@@ -124,6 +126,7 @@ main(int argc, char **argv)
timewarp = 0;
leap = 0;
proxy = NULL;
+ http = 0;
while (1) {
int option_index = 0;
@@ -143,10 +146,11 @@ main(int argc, char **argv)
{"timewarp", 0, 0, 't'},
{"leap", 0, 0, 'l'},
{"proxy", 0, 0, 'x'},
+ {"http", 0, 0, 'w'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "vV::shH:p:P:nC:tlx:",
+ c = getopt_long(argc, argv, "vV::shH:p:P:nC:tlx:w",
long_options, &option_index);
if (c == -1)
break;
@@ -164,6 +168,7 @@ main(int argc, char **argv)
case 't': timewarp = 1; break;
case 'l': leap = 1; break;
case 'x': proxy = optarg; break;
+ case 'w': http = 1; break;
case '?': break;
default : fprintf(stderr, "Unknown option!\n"); usage(); exit(1);
}
@@ -194,6 +199,7 @@ main(int argc, char **argv)
(timewarp ? "timewarp" : "no-fun"),
(leap ? "leapaway" : "holdfast"),
(proxy ? proxy : "none"),
+ (http ? "http" : "tls"),
NULL);
perror("Failed to run tlsdate-helper");
return 1;
diff --git a/src/util.c b/src/util.c
index fe5c370..129de8d 100644
--- a/src/util.c
+++ b/src/util.c
@@ -11,7 +11,9 @@
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
+#ifdef __linux__
#include <linux/rtc.h>
+#endif
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
@@ -143,6 +145,7 @@ wait_with_timeout(int *status, int timeout_secs)
return exited;
}
+#ifdef __linux__
struct rtc_handle
{
int fd;
@@ -226,6 +229,7 @@ int rtc_close(void *handle)
free(h);
return 0;
}
+#endif
int file_write(const char *path, void *buf, size_t sz)
{
@@ -304,10 +308,12 @@ int pgrp_kill(void)
}
static struct platform default_platform = {
+#ifdef __linux__
.rtc_open = rtc_open,
.rtc_write = rtc_write,
.rtc_read = rtc_read,
.rtc_close = rtc_close,
+#endif
.file_write = file_write,
.file_read = file_read,