Merge remote-tracking branch 'upstream/master' into HEAD
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..072f46b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+.deps/
+autom4te.cache/
+Makefile
+Makefile.in
+aclocal.m4
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+missing
+stamp-h1
+config/
+config/tlsdate-config.h
+config/tlsdate-config.h.in
+src/tlsdate-helper.o
+src/tlsdate-helper
+src/tlsdate.o
+src/tlsdate
diff --git a/AUTHORS b/AUTHORS
index 2428418..43ce2ac 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,2 +1,2 @@
-Jacob Appelbaum
+Jacob Appelbaum <jacob@appelbaum.net>
 Christian Grothoff <christian@grothoff.org>
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..8ee112e
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,41 @@
+0.0.2 XXX
+  Add '-n' and '--dont-set-clock' option to fetch but not set time
+  Add '-V' and '--showtime' option to display remote time
+  Add '-t' and '--timewarp' option
+    If the local clock is before RECENT_COMPILE_DATE; we set the clock to the
+    RECENT_COMPILE_DATE. If the local clock is after RECENT_COMPILE_DATE, we
+    leave the clock alone. Clock setting is performed as the first operation
+    and will impact certificate verification. Specifically, this option is
+    helpful if on first boot, the local system clock is set back to the era
+    of Disco and Terrible Hair. This should ensure that 
+    X509_V_ERR_CERT_NOT_YET_VALID or X509_V_ERR_CERT_HAS_EXPIRED are not
+    encountered because of a broken RTC or the lack of a local RTC; we assume
+    that tlsdate is recompiled yearly and that all certificates are otherwise
+    considered valid.
+  Add '-l' and '--leap'
+    Normally, the passing of time or time yet to come ensures that SSL verify
+    functions will fail to validate certificates. Commonly,
+    X509_V_ERR_CERT_NOT_YET_VALID and X509_V_ERR_CERT_HAS_EXPIRED are painfully
+    annoying but still very important error states. When the only issue with
+    the certificates in question is the timing information, this option allows
+    one to trust the remote system's time, as long as it is after
+    RECENT_COMPILE_DATE and before MAX_REASONABLE_TIME. The connection will
+    only be trusted if X509_V_ERR_CERT_NOT_YET_VALID and/or
+    X509_V_OKX509_V_ERR_CERT_HAS_EXPIRED are the only errors encountered. The
+    SSL verify function will not return X509_V_OK if there are any other
+    issues, such as self-signed certificates or if the user pins to a CA that
+    is not used by the remote server. This is useful if your RTC is broken on
+    boot and you are unable to use DNSSEC until you've at least had some kind
+    of leap of cryptographically assured data.
+  Update usage documentation
+  Move {*.c,h} into src/
+  Move *.1 into man/
+  Update TODO list to reflect desired changes
+  Update AppArmor profile to restrict {tlsdate,tlsdate-helper}
+  Update AUTHORS file to include a new email address
+  Update CHANGELOG
+    Added proper date for the 0.0.1 release
+    (Added all of the above items, obviously)
+
+0.0.1 Fri Jul 13, 2012
+  First git tagged release
diff --git a/INSTALL b/INSTALL
index 251561c..798dd9b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -8,3 +8,32 @@
 Cleaning is the usual:
 
   make clean
+
+To make an unsigned Debian package:
+
+  git checkout debian-master
+  make deb
+
+To make a Debian source package:
+
+  git checkout master
+  ./autogen.sh
+  ./configure && make debian_orig
+  git checkout debian-master
+  fakeroot debian/rules clean
+  cd ../
+  dpkg-source -b tlsdate
+
+Example of how to build a package for Debian:
+
+  # First build the source package above
+  scp tlsdate_* dixie.torproject.org:~/src/debian-builds/
+  ~/bin/sbuild-stuff tlsdate_0.0.1-1.dsc
+  # Download or build the package locally
+  # and sign the .changes or .dsc file
+  debsign -k0xD81D840E tlsdate_0.0.1-1.dsc 
+
+Example of how to upload it (after a Debian sponsor signs off on it):
+  dget http://www.example.com/tlsdate_0.0.1-1_amd64.changes
+  dput tlsdate_0.0.1-1_amd64.changes
+
diff --git a/Makefile.am b/Makefile.am
index 10131bf..adff8b8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,5 @@
+# Our Debian version
+DEBIAN_VERSION := $(shell if [ -e debian/changelog ]; then cat debian/changelog|head -n1|cut -d\- -f2| head -c 1; else echo "unknown"; fi)
 AUTOMAKE_OPTIONS = foreign 1.7
 EXTRA_DIST = \
   AUTHORS \
@@ -5,34 +7,29 @@
   LICENSE \
   TODO \
   apparmor-profile \
+  CHANGELOG \
   autogen.sh 
 
-# Our main program
-bin_PROGRAMS = tlsdate tlsdate-helper
-tlsdate_SOURCES = tlsdate.c
-tlsdate_helper_SOURCES = tlsdate-helper.c
-tlsdate_helper_LDADD = -lssl -lcrypto
+SUBDIRS = src
 
 # Our documentation
-man_MANS = tlsdate.1
+man_MANS = man/tlsdate.1 man/tlsdate-helper.1
 EXTRA_DIST += $(man_MANS)
 
-CLEANFILES = tlsdate-config.h.in tlsdate-config.h.in~ \
-  tlsdate-config.h \
-  Makefile.noautoconf \
-  Makefile.in \
-  Makefile \
-  install-sh \
-  depcomp \
-  configure \
-  config.sub \
-  config.status \
-  config.log \
-  config.guess \
-  aclocal.m4 \
-  missing \
-  stamp-h1
+.PHONY: debian_orig git-tag git-push git-tag-debian deb really-clean
+debian_orig:
+	$(MAKE) dist
+	mv tlsdate-$(VERSION).tar.gz ../tlsdate_$(VERSION).orig.tar.gz
 
-# XXX TODO:
-# We also should remove the follow dirs with `make clean`
-# autom4te.cache & .deps
+git-tag:
+	git tag -u 0xD81D840E -s tlsdate-$(VERSION)
+
+git-tag-debian:
+	git tag -u 0xD81D840E -s tlsdate-$(VERSION)-debian-${DEBIAN_VERSION}
+
+git-push:
+	git push --tags
+	git push
+
+deb: debian_orig
+	debuild -rfakeroot -uc -us
diff --git a/README b/README
index d53103e..ad90fe5 100644
--- a/README
+++ b/README
@@ -1,49 +1,76 @@
+tlsdate: secure parasitic rdate replacement
+
+ tlsdate sets the local clock by securely connecting with TLS to remote
+ servers and extracting the remote time out of the secure handshake. Unlike
+ ntpdate, tlsdate uses TCP, for instance connecting to a remote HTTPS or TLS
+ enabled service, and provides some protection against adversaries that try to
+ feed you malicious time information.
+
+Here is an example an unprivileged user fetching the remote time:
+
+  % tlsdate -v -V -n
+  V: tlsdate version 0.0.1
+  V: We were called with the following arguments:
+  V: validate SSL certificates host = www.ptb.de:443
+  V: time is currently 1342197117.577381
+  V: using TLSv1_client_method()
+  V: SSL certificate verification passed
+  V: server time 1342197117 (difference is about 0 s) was fetched in 705 ms
+  Fri Jul 13 18:31:57 CEST 2012
+
+
 This is an example run - starting as root and dropping to nobody:
 
-  % sudo ./tlsdate -v
-  V: tlsdate version 0.1
+  % sudo ./tlsdate -v 
+  V: tlsdate version 0.0.1
   V: We were called with the following arguments:
-  V: ca_racket = 1, verbose = 1, help = 0
-  V: host = www.torproject.org, port = 443
-  V: time is currently 1327923928.555499
-  V: Attempting to verify certificate
-  V: SSL_get_peer_certificate returned
-  V: verification OK: 0
-  V: ssl_verify_result returned 0
-  V: server_random fetched in 0 sec
-  V: server_random with ntohl is: 1327923933.0
-  V: setting time returned: 0
-  V: tlsdate: SUCCESS
+  V: validate SSL certificates host = www.ptb.de:443
+  V: time is currently 1342197222.273552
+  V: using TLSv1_client_method()
+  V: SSL certificate verification passed
+  V: server time 1342197222 (difference is about 0 s) was fetched in 520 ms
+  V: setting time succeeded
 
-Here is an example with a custom host and custom port:
+Here is an example with a custom host and custom port without verification:
 
-  % sudo tlsdate -v -p 80 -H rgnx.net
-  V: tlsdate version 0.1
+  % sudo tlsdate -v --skip-verification -p 80 -H rgnx.net
+  V: tlsdate version 0.0.1
   V: We were called with the following arguments:
-  V: ca_racket = 1, verbose = 1, help = 0
-  V: host = rgnx.net, port = 80
-  V: time is currently 1327923852.540550
-  V: Attempting to verify certificate
-  V: SSL_get_peer_certificate returned
-  E: verification error: 20
-  V: ssl_verify_result returned 20
-  V: server_random fetched in 0 sec
-  V: server_random with ntohl is: 1327923858.0
-  V: setting time returned: 0
-  V: tlsdate: SUCCESS
+  V: disable SSL certificate check host = rgnx.net:80
+  WARNING: Skipping certificate verification!
+  V: time is currently 1342197285.298607
+  V: using TLSv1_client_method()
+  V: Certificate verification skipped!
+  V: server time 1342197286 (difference is about -1 s) was fetched in 765 ms
+  V: setting time succeeded
 
 Here is an example of a false ticker that is detected and rejected:
 
   % sudo tlsdate -v -H facebook.com
-  V: tlsdate version 0.1
+  V: tlsdate version 0.0.1
   V: We were called with the following arguments:
-  V: validate SSL certificates host = www.facebook.com:443
-  V: time is currently 1329350443.903045
+  V: validate SSL certificates host = facebook.com:443
+  V: time is currently 1342197379.931852
   V: using TLSv1_client_method()
   V: SSL certificate verification passed
-  V: server time 3599738087 (difference is about 2024579652 s) was fetched in
-  1287 ms
+  V: server time 2693501503 (difference is about -1351304124 s) was fetched in 724 ms
   remote server is a false ticker from the future!
 
-"It's a piece of shit but It Works." - this is not yet secure and it needs
-an audit before it should be used for anything, ever.
+Here is an example where a system may not have any kind of RTC at boot. Do the
+time warp to restore sanity and do so with a leap of faith:
+
+  % sudo tlsdate -v -V -l -t
+  V: tlsdate version 0.0.1
+  V: We were called with the following arguments:
+  V: validate SSL certificates host = www.ptb.de:443
+  V: RECENT_COMPILE_DATE is 1342387410.000000
+  V: time is currently 1342404491.536366
+  V: time is greater than RECENT_COMPILE_DATE
+  V: using TLSv1_client_method()
+  V: freezing time for x509 verification
+  V: remote peer provided: 1342404491, prefered over compile time: 1342387410
+  V: SSL certificate verification passed
+  V: server time 1342404491 (difference is about 0 s) was fetched in 525 ms
+  Sun Jul 15 22:08:11 EDT 2012
+  V: setting time succeeded
+
diff --git a/TODO b/TODO
index c459d1d..9a8cd48 100644
--- a/TODO
+++ b/TODO
@@ -1,21 +1,16 @@
 
 Here is a nice list of things to do to improve tlsdate:
 
- 0)   relocate source and configs into subdirs
-        move code into src/
-        move configs into configs/
 
  1)  hack the client handshake to not leak the clock to the server
        set it to all zeros or something cute or something random
 
- 2)  allow users to pass certs for custom verification
  3)  add HTTP GET request to avoid network fingerprinting
  5)  daemonize and regularly slam the clock
  6)  skew the clock rather than slamming it
  7)  drop privs earlier
- 8)  audit the code for show stopping bugs
  9)  make this work with Tor in a proxy safe manner (no DNS mode)
-10)  pin SSL certs for racket mode
+       This should work with torsocks, does it? Audit results please!
 11)  verification of remote certificate for Tor nodes
 13)  account for servers that do not send UTC (Microsoft sends local time)
 14)  port to nss, gnutls, yassl and other libraries
@@ -24,7 +19,8 @@
 17)  find others to audit it - we need more eyes!
 18)  cache recent time to /tmp/tlsdate_stamp or something
 19)  override client time
-       ensure we only believe the time to be the one compiled in or...
-       we read the cache of the time and read that.
+       Add a way to read the cache of the last updated time and read that as
+       the new epoch.
+20)  Add verification of remote servers by DANE/CAA DNSSEC protected records
 
 Patches welcome!
diff --git a/apparmor-profile b/apparmor-profile
index 8cd3efa..681906b 100644
--- a/apparmor-profile
+++ b/apparmor-profile
@@ -7,7 +7,7 @@
 #
 
 #include <tunables/global>
-/usr/sbin/tlsdate {
+/usr/bin/tlsdate {
   #include <abstractions/consoles>
   #include <abstractions/ssl_certs>
 
@@ -20,9 +20,10 @@
   network inet stream,
   # IPv4 UDP for DNS resolution
   network inet dgram,
-  # No ipv6 TCP or UDP yet
-  deny network inet6 stream,
-  deny network inet6 dgram,
+  # IPv6 TCP
+  network inet6 stream,
+  # IPv6 UDP
+  network inet6 dgram,
 
   # Required for gethostbyname
   /etc/resolv.conf r,
@@ -38,6 +39,9 @@
 
   # Required for getpwnam
   /etc/passwd r,
+  /etc/group r,
+  /proc/sys/kernel/ngroups_max r,
+
 
   # Allow reading of libs and /tmp
   /etc/ld.so.cache r,
@@ -57,4 +61,61 @@
   /tmp/ r,
   owner /tmp/tlsdate_*/ rw,
 
+  # We'll allow tlsdate to exec tlsdate-helper
+  /usr/bin/tlsdate-helper ixm,
+  /usr/bin/tlsdate ixm,
+}
+
+/usr/bin/tlsdate-helper {
+  #include <abstractions/consoles>
+  #include <abstractions/ssl_certs>
+
+  capability sys_time,
+  capability setgid,
+  capability setuid,
+  capability sys_chroot,
+
+  # IPv4 TCP
+  network inet stream,
+  # IPv4 UDP for DNS resolution
+  network inet dgram,
+  # IPv6 TCP
+  network inet6 stream,
+  # IPv6 UDP
+  network inet6 dgram,
+
+  # Required for gethostbyname
+  /etc/resolv.conf r,
+  /etc/nsswitch.conf r,
+  /etc/localtime r,
+  /etc/nsswitch.conf r,
+  /etc/hosts r,
+  /etc/host.conf r,
+
+  # Allow reading public certs but not private keys
+  /etc/ssl/certs/* r,
+  /usr/share/ca-certificates/*/** r,
+
+  # Required for getpwnam
+  /etc/passwd r,
+  /etc/group r,
+
+
+  # Allow reading of libs and /tmp
+  /etc/ld.so.cache r,
+
+  # Random number generation requires these two
+  /dev/random r,
+  /dev/urandom r,
+
+  # Allow mapping of shared libraries
+  /lib/* rm,
+  /lib32/* rm,
+  /lib64/* rm,
+  /usr/lib/* rm,
+  /lib/x86_64-linux-gnu/* rm,
+
+  # We'll allow tlsdate to write a new root to chroot into
+  /tmp/ r,
+  owner /tmp/tlsdate_*/ rw,
 }
diff --git a/autogen.sh b/autogen.sh
index 5de05af..345ccc2 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -6,7 +6,12 @@
 
 set -e
 
-exec autoreconf -ivf && \
+if [ ! -d config ];
+then
+  mkdir config;
+fi
+
+exec autoreconf -ivf -I src -I config && \
 aclocal && \
 autoheader && \
 autoconf && \
diff --git a/configure.ac b/configure.ac
index b62c498..a831447 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,7 @@
-AC_INIT([tlsdate], [0.1], [jacob at appelbaum.net])
-AM_INIT_AUTOMAKE([tlsdate], [0.1])
-AM_CONFIG_HEADER([tlsdate-config.h])
+AC_INIT([tlsdate], [0.0.1], [jacob at appelbaum.net])
+AC_CONFIG_AUX_DIR(config)
+AM_CONFIG_HEADER([config/tlsdate-config.h])
+AM_INIT_AUTOMAKE([tlsdate], [0.0.1])
 AC_CANONICAL_HOST
 AH_TOP([#define _GNU_SOURCE  1])
 
@@ -11,6 +12,15 @@
 CFLAGS+=" --param ssp-buffer-size=1 "
 LDFLAGS=" -pie -z relro -z now "
 
+dnl Here we should build a small program to fetch the build system time in a portable
+dnl manner. We have no Win32 users, we can fix this if we ever find one that
+dnl cares.
+COMPILE_DATE=`date +%s`
+AC_SUBST(COMPILE_DATE)
+AC_DEFINE_UNQUOTED([RECENT_COMPILE_DATE],
+                   [(uint32_t) ${COMPILE_DATE}],
+                   [Time in seconds since the Disco epoch at build time])
+
 # Required headers
 AC_CHECK_HEADERS(
   stdio.h  \
@@ -32,7 +42,6 @@
   openssl/evp.h 
 , , AC_MSG_ERROR([Required headers missing; compilation will not succeed]))
 
-AC_CHECK_FUNCS([setresuid])
+AC_CHECK_FUNCS([setresuid gettimeofday])
 
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT
+AC_OUTPUT(Makefile src/Makefile)
diff --git a/man/tlsdate-helper.1 b/man/tlsdate-helper.1
new file mode 100644
index 0000000..4d48677
--- /dev/null
+++ b/man/tlsdate-helper.1
@@ -0,0 +1,30 @@
+.\" Process this file with
+.\" groff -man -Tascii foo.1
+.\"
+.TH TLSDATE 1 "JANUARY 2011" Linux "User Manuals"
+.SH NAME
+tlsdate-helper \- secure parasitic rdate replacement
+.SH SYNOPSIS
+.B tlsdate-helper host port protocol ca_racket verbose certdir setclock \
+showtime timewarp leapaway
+.SH DESCRIPTION
+.B tlsdate-helper
+is a tool for setting the system clock by hand or by communication
+with the network. It does not set the Real Time Clock. It is designed to be as
+secure as TLS (RFC 2246) but of course the security of TLS is often reduced to
+whichever CA racket you believe is trustworthy. By default, tlsdate-helper
+trusts your local CA root store - so any of these companies could assist in a
+MITM attack against you and you'd be screwed.
+
+This tool is designed to be run by hand or as a system daemon. It must be
+run as root or otherwise have the proper caps; it will not be able to set
+the system time without running as root or another privileged user.
+.SH BUGS
+It's likely! Let us know by contacting jacob@appelbaum.net
+
+Note that
+.B tlsdate(1)
+is still in Alpha, and may not work as expected.
+.SH AUTHOR
+Jacob Appelbaum <jacob at appelbaum dot net>
+.SH "SEE ALSO"
diff --git a/man/tlsdate.1 b/man/tlsdate.1
new file mode 100644
index 0000000..7c43fcd
--- /dev/null
+++ b/man/tlsdate.1
@@ -0,0 +1,78 @@
+.\" Process this file with
+.\" groff -man -Tascii foo.1
+.\"
+.TH TLSDATE 1 "JANUARY 2011" Linux "User Manuals"
+.SH NAME
+tlsdate \- secure parasitic rdate replacement
+.SH SYNOPSIS
+.B tlsdate [-hnvVstl] [-H [hostname]] [-p [port]] [-P [sslv23|sslv3|tlsv1]] \
+[--certdir [dirname]]
+.SH DESCRIPTION
+.B tlsdate
+is a tool for setting the system clock by hand or by communication
+with the network. It does not set the Real Time Clock. It is designed to be as
+secure as TLS (RFC 2246) but of course the security of TLS is often reduced to
+whichever CA racket you believe is trustworthy. By default, tlsdate trusts your
+local CA root store - so any of these companies could assist in a MITM attack
+against you and you'd be screwed.
+
+This tool is designed to be run by hand or as a system daemon. It must be
+run as root or otherwise have the proper caps; it will not be able to set
+the system time without running as root or another privileged user.
+.SH OPTIONS
+.IP "-h | --help"
+Print the help message
+.IP "-s | --skip-verification"
+Skip certificate verification
+.IP "-H | --host [hostname|ip]"
+Set remote hostname (default: 'www.ptb.de')
+.IP "-n | --dont-set-clock"
+Do not set the system clock to the time of the remote server
+.IP "-p | --port [port]"
+Set remote port (default: '443')
+.IP "-P | --protocol [sslv23|sslv3|tlsv1]"
+Set protocol to use when communicating with server (default: 'tlsv1')
+.IP "-C | --certdir [dirname]"
+Set the local directory where certificates are located
+(default: '/etc/ssl/certs')
+This allows for certificate or certificate authority (CA) pinning. To ensure
+that signatures are only valid if they are signed by a specific CA or
+certificate, set the path to a directory containing only the desired
+certificates.
+.IP "-v | --verbose"
+Provide verbose output
+.IP "-V | --showtime"
+Show the time retrieved from the remote server
+.IP "-t | --timewarp"
+If the local clock is before RECENT_COMPILE_DATE; we set the clock to the
+RECENT_COMPILE_DATE. If the local clock is after RECENT_COMPILE_DATE, we leave
+the clock alone. Clock setting is performed as the first operation and will
+impact certificate verification. Specifically, this option is helpful if on
+first boot, the local system clock is set back to the era of Disco and Terrible
+Hair. This should ensure that X509_V_ERR_CERT_NOT_YET_VALID or
+X509_V_ERR_CERT_HAS_EXPIRED are not encountered because of a broken RTC or the
+lack of a local RTC; we assume that tlsdate is recompiled yearly and that all
+certificates are otherwise considered valid.
+.IP "-l | --leap"
+Normally, the passing of time or time yet to come ensures that SSL verify
+functions will fail to validate certificates. Commonly,
+X509_V_ERR_CERT_NOT_YET_VALID and X509_V_ERR_CERT_HAS_EXPIRED are painfully
+annoying but still very important error states. When the only issue with the
+certificates in question is the timing information, this option allows you to
+trust the remote system's time, as long as it is after RECENT_COMPILE_DATE and
+before MAX_REASONABLE_TIME. The connection will only be trusted if
+X509_V_ERR_CERT_NOT_YET_VALID and/or X509_V_OKX509_V_ERR_CERT_HAS_EXPIRED are
+the only errors encountered. The SSL verify function will not return X509_V_OK
+if there are any other issues, such as self-signed certificates or if the user
+pins to a CA that is not used by the remote server. This is useful if your RTC
+is broken on boot and you are unable to use DNSEC until you've at least had
+some kind of leap of cryptographically assured data.
+.SH BUGS
+It's likely! Let us know by contacting jacob@appelbaum.net
+
+Note that
+.B tlsdate(1)
+is still in Alpha, and may not work as expected.
+.SH AUTHOR
+Jacob Appelbaum <jacob at appelbaum dot net>
+.SH "SEE ALSO"
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..756704f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = foreign 1.7
+
+# Our main program
+bin_PROGRAMS = tlsdate tlsdate-helper
+tlsdate_SOURCES = tlsdate.c
+tlsdate_helper_SOURCES = tlsdate-helper.c
+tlsdate_helper_LDADD = -lssl -lcrypto
+
+# We're not shipping headers
+noinst_HEADERS = tlsdate.h
diff --git a/tlsdate-helper.c b/src/tlsdate-helper.c
similarity index 73%
rename from tlsdate-helper.c
rename to src/tlsdate-helper.c
index a547be5..16d51b8 100644
--- a/tlsdate-helper.c
+++ b/src/tlsdate-helper.c
@@ -73,7 +73,7 @@
  * the system time without running as root or another privileged user.
  */
 
-#include "tlsdate-config.h"
+#include "../config/tlsdate-config.h"
 
 #include <stdarg.h>
 #include <stdint.h>
@@ -94,15 +94,24 @@
 #include <openssl/evp.h>
 
 /** Name of user that we feel safe to run SSL handshake with. */
+#ifndef UNPRIV_USER
 #define UNPRIV_USER "nobody"
+#endif
+#ifndef UNPRIV_GROUP
 #define UNPRIV_GROUP "nogroup"
+#endif
 
 // We should never accept a time before we were compiled
 // We measure in seconds since the epoch - eg: echo `date '+%s'`
 // We set this manually to ensure others can reproduce a build;
 // automation of this will make every build different!
-#define RECENT_COMPILE_DATE (uint32_t) 1328610583
+#ifndef RECENT_COMPILE_DATE
+#define RECENT_COMPILE_DATE (uint32_t) 1342323666
+#endif
+
+#ifndef MAX_REASONABLE_TIME
 #define MAX_REASONABLE_TIME (uint32_t) 1999991337
+#endif
 
 // After the duration of the TLS handshake exceeds this threshold
 // (in msec), a warning is printed.
@@ -118,6 +127,8 @@
 
 static const char *protocol;
 
+static const char *certdir;
+
 /** helper function to print message and die */
 static void
 die(const char *fmt, ...)
@@ -152,12 +163,13 @@
  * @param time_map where to store the current time
  */
 static void
-run_ssl (uint32_t *time_map)
+run_ssl (uint32_t *time_map, int time_is_an_illusion)
 {
   BIO *s_bio;
-  BIO *c_bio;
   SSL_CTX *ctx;
   SSL *ssl;
+  uint32_t compiled_time = RECENT_COMPILE_DATE;
+  uint32_t max_reasonable_time = MAX_REASONABLE_TIME;
 
   SSL_load_error_strings();
   SSL_library_init();
@@ -183,9 +195,7 @@
 
   if (ca_racket)
   {
-    // For google specifically:
-    // SSL_CTX_load_verify_locations(ctx, "/etc/ssl/certs/Equifax_Secure_CA.pem", NULL);
-    if (1 != SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs/"))
+    if (1 != SSL_CTX_load_verify_locations(ctx, NULL, certdir))
       fprintf(stderr, "SSL_CTX_load_verify_locations failed\n");
   }
 
@@ -199,8 +209,8 @@
        (1 != BIO_set_conn_port(s_bio, port)) )
     die ("Failed to initialize connection to `%s:%s'\n", host, port);
 
-  if (NULL == (c_bio = BIO_new_fp(stdout, BIO_NOCLOSE)))
-    die ("FIXME: error message");
+  if (NULL == BIO_new_fp(stdout, BIO_NOCLOSE))
+    die ("BIO_new_fp returned error, possibly: %s", strerror(errno));
 
   // This should run in seccomp
   // eg:     prctl(PR_SET_SECCOMP, 1);
@@ -210,12 +220,37 @@
     die ("SSL handshake failed\n");
   // Verify the peer certificate against the CA certs on the local system
   if (ca_racket) {
-    X509 *x509;
     long ssl_verify_result;
 
-    if (NULL == (x509 = SSL_get_peer_certificate(ssl)) )
+    if (NULL == SSL_get_peer_certificate(ssl))
+    {
       die ("Getting SSL certificate failed\n");
-
+    }
+    if (time_is_an_illusion)
+    {
+      // XXX TODO: If we want to trust the remote system for time,
+      // can we just read that time out of the remote system and if the
+      // cert verifies, decide that the time is reasonable?
+      // Such a process seems to indicate that a once valid cert would be
+      // forever valid - we stopgap that by ensuring it isn't less than
+      // the latest compiled_time and isn't above max_reasonable_time...
+      // XXX TODO: Solve eternal question about the Chicken and the Egg...
+      verb("V: freezing time for x509 verification\n");
+      memcpy(time_map, ssl->s3->server_random, sizeof (uint32_t));
+      if (compiled_time < ntohl( * time_map)
+          &&
+          ntohl(*time_map) < max_reasonable_time)
+      {
+        verb("V: remote peer provided: %d, prefered over compile time: %d\n",
+              ntohl( *time_map), compiled_time);
+        // In theory, we instruct verify to check if it _would be valid_ if the
+        // verification happened at ((time_t) ntohl(*time_map))
+        X509_VERIFY_PARAM_set_time(ctx->param, (time_t) ntohl(*time_map));
+      } else {
+        die("V: the remote server is a false ticker! server: %d compile: %d\n",
+             ntohl(*time_map), compiled_time);
+      }
+    }
     // In theory, we verify that the cert is valid
     ssl_verify_result = SSL_get_verify_result(ssl);
     switch (ssl_verify_result)
@@ -233,9 +268,8 @@
   } else {
     verb ("V: Certificate verification skipped!\n");
   }
-
   // from /usr/include/openssl/ssl3.h
-  //  ssl->s3->server_random is an unsigned char of 32 bytes
+  //  ssl->s3->server_random is an unsigned char of 32 bits
   memcpy(time_map, ssl->s3->server_random, sizeof (uint32_t));
 }
 
@@ -295,18 +329,43 @@
   uint32_t *time_map;
   struct timeval start_timeval;
   struct timeval end_timeval;
+  struct timeval warp_time;
   int status;
   pid_t ssl_child;
   long long rt_time_ms;
   uint32_t server_time_s;
+  int setclock;
+  int showtime;
+  int timewarp;
+  int leap;
 
-  if (argc != 6)
+  if (argc != 11)
     return 1;
   host = argv[1];
   port = argv[2];
   protocol = argv[3];
+  certdir = argv[6];
   ca_racket = (0 != strcmp ("unchecked", argv[4]));
   verbose = (0 != strcmp ("quiet", argv[5]));
+  setclock = (0 == strcmp ("setclock", argv[7]));
+  showtime = (0 == strcmp ("showtime", argv[8]));
+  timewarp = (0 == strcmp ("timewarp", argv[9]));
+  leap = (0 == strcmp ("leapaway", argv[10]));
+
+  if (timewarp)
+  {
+    warp_time.tv_sec = RECENT_COMPILE_DATE;
+    warp_time.tv_usec = 0;
+    verb ("V: RECENT_COMPILE_DATE is %lu.%06lu\n",
+         (unsigned long)warp_time.tv_sec,
+         (unsigned long)warp_time.tv_usec);
+  }
+
+  /* We are not going to set the clock, thus no need to stay root */
+  if (0 == setclock && 0 == timewarp)
+  {
+    become_nobody ();
+  }
 
   time_map = mmap (NULL, sizeof (uint32_t),
        PROT_READ | PROT_WRITE,
@@ -314,16 +373,45 @@
   if (MAP_FAILED == time_map)
   {
     fprintf (stderr, "mmap failed: %s\n",
-       strerror (errno));
+             strerror (errno));
     return 1;
   }
 
   /* Get the current time from the system clock. */
   if (0 != gettimeofday(&start_timeval, NULL))
+  {
     die ("Failed to read current time of day: %s\n", strerror (errno));
+  }
+
   verb ("V: time is currently %lu.%06lu\n",
-  (unsigned long)start_timeval.tv_sec,
-  (unsigned long)start_timeval.tv_usec);
+       (unsigned long)start_timeval.tv_sec,
+       (unsigned long)start_timeval.tv_usec);
+
+  if (((unsigned long)start_timeval.tv_sec) < ((unsigned long)warp_time.tv_sec))
+  {
+    verb ("V: local clock time is less than RECENT_COMPILE_DATE\n");
+    if (timewarp)
+    {
+      verb ("V: Attempting to warp local clock into the future\n");
+      if (0 != settimeofday(&warp_time, NULL))
+      {
+        die ("setting time failed: %s (Attempted to set clock to %lu.%06lu)\n",
+        strerror (errno),
+        (unsigned long)warp_time.tv_sec,
+        (unsigned long)warp_time.tv_usec);
+      }
+      if (0 != gettimeofday(&start_timeval, NULL))
+      {
+        die ("Failed to read current time of day: %s\n", strerror (errno));
+      }
+      verb ("V: time is currently %lu.%06lu\n",
+           (unsigned long)start_timeval.tv_sec,
+           (unsigned long)start_timeval.tv_usec);
+      verb ("V: It's just a step to the left...\n");
+    }
+  } else {
+    verb ("V: time is greater than RECENT_COMPILE_DATE\n");
+  }
 
   /* initialize to bogus value, just to be on the safe side */
   *time_map = 0;
@@ -335,7 +423,7 @@
   if (0 == ssl_child)
   {
     become_nobody ();
-    run_ssl (time_map);
+    run_ssl (time_map, leap);
     (void) munmap (time_map, sizeof (uint32_t));
     _exit (0);
   } 
@@ -365,7 +453,19 @@
       "server or run it again\n", TLS_RTT_THRESHOLD);
   }
 
+  if (showtime)
+  {
+     struct tm  ltm;
+     time_t tim = server_time_s;
+     char       buf[256];
+
+     localtime_r(&tim, &ltm);
+     (void) strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %Z %Y", &ltm);
+     fprintf(stdout, "%s\n", buf);
+  }
+
   /* finally, actually set the time */
+  if (setclock)
   {
     struct timeval server_time;
 
@@ -381,9 +481,10 @@
     if (server_time.tv_sec <= RECENT_COMPILE_DATE)
       die ("remote server is a false ticker!\n");
     if (0 != settimeofday(&server_time, NULL))
-      die ("setting time failed: %s\n", strerror (errno));
+      die ("setting time failed: %s (Difference from server is about %d)\n",
+     strerror (errno),
+     start_timeval.tv_sec - server_time_s);
+    verb ("V: setting time succeeded\n");
   }
-  verb ("V: setting time succeeded\n");
   return 0;
 }
-
diff --git a/tlsdate.c b/src/tlsdate.c
similarity index 83%
rename from tlsdate.c
rename to src/tlsdate.c
index c457f80..5212da6 100644
--- a/tlsdate.c
+++ b/src/tlsdate.c
@@ -73,7 +73,7 @@
  * the system time without running as root or another privileged user.
  */
 
-#include "tlsdate-config.h"
+#include "../config/tlsdate-config.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -84,6 +84,7 @@
 #define DEFAULT_HOST "www.ptb.de"
 #define DEFAULT_PORT "443"
 #define DEFAULT_PROTOCOL "tlsv1"
+#define DEFAULT_CERTDIR "/etc/ssl/certs"
 
 /** Return the proper commandline switches when the user needs information. */
 static void
@@ -92,10 +93,15 @@
   fprintf(stderr, "tlsdate usage:\n"
           " [-h|--help]\n"
           " [-s|--skip-verification]\n"
+          " [-n|--dont-set-clock]\n"
           " [-H|--host] [hostname|ip]\n"
           " [-p|--port] [port number]\n"
-          " [-P]--protocol] [sslv23|sslv3|tlsv1]\n"
-          " [-v|--verbose]\n");
+          " [-P|--protocol] [sslv23|sslv3|tlsv1]\n"
+          " [-C|--certdir] [dirname]\n"
+          " [-v|--verbose]\n"
+          " [-V|--showtime]\n"
+          " [-t|--timewarp]\n"
+          " [-l|--leap]\n");
 }
 
 
@@ -104,15 +110,25 @@
 {
   int verbose;
   int ca_racket;
+  int showtime;
+  int setclock;
   const char *host;
   const char *port;
   const char *protocol;
+  const char *certdir;
+  int timewarp;
+  int leap;
 
   host = DEFAULT_HOST;
   port = DEFAULT_PORT;
   protocol = DEFAULT_PROTOCOL;
+  certdir = DEFAULT_CERTDIR;
   verbose = 0;
   ca_racket = 1;
+  showtime = 0;
+  setclock = 1;
+  timewarp = 0;
+  leap = 0;
 
   while (1) {
     int option_index = 0;
@@ -121,26 +137,36 @@
     static struct option long_options[] =
       {
         {"verbose", 0, 0, 'v'},
+        {"showtime", 0, 0, 'V'},
         {"skip-verification", 0, 0, 's'},
         {"help", 0, 0, 'h'},
         {"host", 0, 0, 'H'},
         {"port", 0, 0, 'p'},
         {"protocol", 0, 0, 'P'},
+        {"dont-set-clock", 0, 0, 'n'},
+        {"certdir", 0, 0, 'C'},
+        {"timewarp", 0, 0, 't'},
+        {"leap", 0, 0, 'l'},
         {0, 0, 0, 0}
       };
 
-    c = getopt_long(argc, argv, "vshH:p:P:",
+    c = getopt_long(argc, argv, "vVshH:p:P:nC:tl",
                     long_options, &option_index);
     if (c == -1)
       break;
 
     switch (c) {
       case 'v': verbose = 1; break;
+      case 'V': showtime = 1; break;
       case 's': ca_racket = 0; break;
       case 'h': usage(); exit(1); break;
       case 'H': host = optarg; break;
       case 'p': port = optarg; break;
       case 'P': protocol = optarg; break;
+      case 'n': setclock = 0; break;
+      case 'C': certdir = optarg; break;
+      case 't': timewarp = 1; break;
+      case 'l': leap = 1; break;
       case '?': break;
       default : fprintf(stderr, "Unknown option!\n"); usage(); exit(1);
     }
@@ -165,8 +191,12 @@
     protocol,
     (ca_racket ? "racket" : "unchecked"),
     (verbose ? "verbose" : "quiet"),
+    certdir,
+    (setclock ? "setclock" : "dont-set-clock"),
+    (showtime ? "showtime" : "no-showtime"),
+    (timewarp ? "timewarp" : "no-fun"),
+    (leap ? "leapaway" : "holdfast"),
     NULL);
   perror("Failed to run tlsdate-helper");
   return 1;
 }
-
diff --git a/tlsdate.h b/src/tlsdate.h
similarity index 83%
rename from tlsdate.h
rename to src/tlsdate.h
index 37444b5..f2704bf 100644
--- a/tlsdate.h
+++ b/src/tlsdate.h
@@ -7,8 +7,8 @@
   * \brief The main header for our clock helper.
   **/
 
-#ifndef _TORDATE_H
-#define _TORDATE_H
+#ifndef _TLSDATE_H
+#define _TLSDATE_H
 
 #include <stdint.h>
 #include <stdio.h>
@@ -16,7 +16,7 @@
 #include <getopt.h>
 #include <time.h>
 
-/** The current version of tor-time. */
+/** The current version of tlsdate. */
 #define tlsdate_version "0.1"
 
 /** This is where we store parsed commandline options. */
@@ -24,6 +24,8 @@
   int verbose;
   int ca_racket;
   int help;
+  int showtime;
+  int setclock;
   time_t manual_time;
   char *host;
   char *port;
diff --git a/tlsdate.1 b/tlsdate.1
deleted file mode 100644
index 56c6f30..0000000
--- a/tlsdate.1
+++ /dev/null
@@ -1,42 +0,0 @@
-.\" Process this file with
-.\" groff -man -Tascii foo.1
-.\"
-.TH TLSDATE 1 "JANUARY 2011" Linux "User Manuals"
-.SH NAME
-tlsdate \- secure parasitic rdate replacement
-.SH SYNOPSIS
-.B tlsdate [-hvs] [-H [hostname]] [-p [port]] [-P [sslv23|sslv3|tlsv1]]
-.SH DESCRIPTION
-.B tlsdate
-is a tool for setting the system clock by hand or by communication
-with the network. It does not set the Real Time Clock. It is designed to be as
-secure as TLS (RFC 2246) but of course the security of TLS is often reduced to
-whichever CA racket you believe is trustworthy. By default, tlsdate trusts your
-local CA root store - so any of these companies could assist in a MITM attack
-against you and you'd be screwed.
-
-This tool is designed to be run by hand or as a system daemon. It must be
-run as root or otherwise have the proper caps; it will not be able to set
-the system time without running as root or another privileged user.
-.SH OPTIONS
-.IP -h | --help
-Print the help message
-.IP -s | --skip-verification
-Skip certificate verification
-.IP -H | --host [hostname|ip]
-Set remote hostname (default: 'www.torproject.org')
-.IP -p | --port [port]
-Set remote port (default: '443')
-.IP -P | --protocol [sslv23|sslv3|tlsv1]
-Set protocol to use when communicating with server (default: 'tlsv1')
-.IP -v | --verbose
-Provide verbose output
-.SH BUGS
-It's likely! Let us know by contacting jacob@appelbaum.net
-
-Note that
-.B tlsdate(1)
-is still in Alpha, and may not work as expected.
-.SH AUTHOR
-Jacob Appelbaum <jacob at appelbaum dot net>
-.SH "SEE ALSO"