Use epoch timestamp from GPG instead of parsing the display timestamp
authorJacob Bachmeyer <jcb@gnu.org>
Wed, 26 Oct 2022 04:26:57 +0000 (23:26 -0500)
committerJacob Bachmeyer <jcb@gnu.org>
Wed, 26 Oct 2022 04:26:57 +0000 (23:26 -0500)
The --status-fd option reports timestamps in epoch time directly, although
the documentation indicates that ISO 8601 format is also possible.  This
change eliminates several manipulations and removes the dependency on the
external Date::Manip module.

gatekeeper.pl

index 31622b5eb580dc9e8a113c060ca673946144336c..89a95c8e5a815a4996327fe56f4a4e7d1d2ba5ad 100755 (executable)
@@ -176,7 +176,6 @@ use File::Spec;
 use Pod::Usage;
 
 use Net::SMTP;
-use Date::Manip;
 use Sys::Syslog qw(:DEFAULT setlogsock);
 use Getopt::Long;
 use Text::Wrap;
@@ -1351,7 +1350,14 @@ The 64-bit long key ID of the key that signed TEXT, if available.
 
 The fingerprint of the PGP key that signed TEXT, if available.
 
-=item ...
+=item sig_creation
+
+Epoch timestamp of signature.
+
+=item sig_expiration
+
+Epoch timestamp at which the signature expires, if the signature expires.
+This key is only present if the signature has an expiration date.
 
 
 =back
@@ -1556,9 +1562,13 @@ sub verify_clearsigned_message {
          if 0 == $ret{exitcode};
       } elsif ($1 eq 'ERR') {          # an ERRSIG line
        $verdict_status++;
-       if (m/\G(\d+)\s(\d+)\s([[:xdigit:]]{2})\s([-:T[:digit:]]+)\s(\d+)/gc) {
+       if (m/\G(\d+)\s(\d+)\s([[:xdigit:]]{2})\s([-:T[:digit:]Z+]+)\s(\d+)
+            /gcx) {
        #  $1 -- pubkey algorithm        $2 -- digest algorithm
        #  $3 -- timestamp               $4 -- result code
+         ftp_abort('gpgv returned an ISO8601 timestamp; implementation needed')
+           if $3 =~ m/T/;
+         $ret{sig_creation} = $3;
        } else
          { push @{$ret{TILT}}, 'gpgv ERRSIG line failed parsing' }
 
@@ -1568,7 +1578,7 @@ sub verify_clearsigned_message {
        $check_status++;
       }
     } elsif (m/\G(VALID)SIG\s([[:xdigit:]]+)\s(\d{4}-\d{2}-\d{2})\s
-              ([-:T[:digit:]]+)\s([-:T[:digit:]]+)\s(\d+)\s(\S+)\s
+              ([-:T[:digit:]Z+]+)\s([-:T[:digit:]Z+]+)\s(\d+)\s(\S+)\s
               (\d+)\s(\d+)\s([[:xdigit:]]{2})\s([[:xdigit:]]+)
              /gcx) {
       $verdict_status++;
@@ -1580,6 +1590,11 @@ sub verify_clearsigned_message {
       # $11 -- primary key fingerprint
       $ret{key_fingerprint} = $2;
       $ret{key_longid} = substr $2,-16;
+      ftp_abort('gpgv returned an ISO8601 timestamp; implementation needed')
+       if $4 =~ m/T/ || $5 =~ m/T/;
+      $ret{sig_creation} = $4;
+      # GPG reports 0 if the signature does not expire
+      $ret{sig_expiration} = $5 if $5 > 0;
     }
   }
   close $status or ftp_abort('close in-memory file for gpgv status');
@@ -1917,33 +1932,16 @@ sub read_directive_file {
   # than the one for the last file that was uploaded
   # This is only relevant when a 'filename' directive is present, hence the
   # test of the $filename_required variable.
-  if (($result->{raw_log} =~ /Signature made (.*)/)
+  if (defined $result->{sig_creation}
       && (exists($info{filename}))) {
-    my $timestr = $1;
-
-    ftp_syslog('debug', "DEBUG: Signature made $1") if DEBUG;
-
-    # Some versions of GPG also mention the key used to make the signature
-    # on this line, while others do not.  The testing mock does.
-    $timestr =~ s/ using .*//; # trim to only timestamp
-
-    # If the time/date string starts with a weekday
-    # (e.g. "Wed Apr 28 16:40:03 2004 EDT"), chop off the weekday -
-    # Date::Manip doesn't like it
-    $timestr =~ s/^[a-z]+? ([a-z]+)/$1/i;
-
-    # We need to convert time/date strings like "Apr 28 16:40:03 2004 EDT" into
-    # "Apr 28 16:40:03 2004 EDT" for Date::Manip to understand them...
-    $timestr =~
-      s/^([a-z]+? +\d{1,2}) (\d{2}:\d{2}:\d{2}) (\d{4}) (.*)$/$1 $3 $2 $4/i;
 
-    my $date = ParseDate($timestr);
-    my $epoch = UnixDate($date,"%s");
+    ftp_syslog('debug', "DEBUG: Signature made "
+              .strftime('%a %b %d %H:%M:%S %Y %Z',
+                        localtime $result->{sig_creation})) if DEBUG;
 
     # Verify that this timestamp is not too far in the future. We allow a
     # discrepancy of 1 day so we don't have to worry about timezones
-    my $now = time();
-    if ($epoch > ($now + 24*3600)) {
+    if ($result->{sig_creation} > (time() + 24*3600)) {
       fatal("GPG signed upload from the future - not allowed. "
            ."Please make sure your clock is set correctly, "
            ."resign the directive file, and upload again. "
@@ -1954,7 +1952,7 @@ sub read_directive_file {
     my $full_filename = $info{"directory"} . '/' . $uploaded_file;
     $full_filename =~ s/\/\//\//g; # Just in case...
 
-    advance_timestamp_ratchet($full_filename, $epoch);
+    advance_timestamp_ratchet($full_filename, $result->{sig_creation});
   } elsif (exists($info{filename})) {
     fatal("gpg verification problem: could not extract timestamp",1);
   }