commit 16ee83468552bee9205d6de6b3c4633b160986d6 Author: Nick Mathewson nickm@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,