[tor-commits] [tlsdate/master] Rudimentary support for HTTP Date headers

ioerror at torproject.org ioerror at torproject.org
Thu Oct 31 10:25:58 UTC 2013


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





More information about the tor-commits mailing list