]> git.saurik.com Git - apt.git/commitdiff
implement Signed-By without using gpg for verification
authorDavid Kalnischkies <david@kalnischkies.de>
Tue, 7 Jul 2015 20:11:20 +0000 (22:11 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Mon, 10 Aug 2015 15:25:26 +0000 (17:25 +0200)
The previous commit returns to the possibility of using just gpgv for
verification proposes. There is one problem through: We can't enforce a
specific keyid without using gpg, but our acquire method can as it
parses gpgv output anyway, so it can deal with good signatures from not
expected signatures and treats them as unknown keys instead.

Git-Dch: Ignore

methods/gpgv.cc
test/integration/test-apt-key
test/integration/test-releasefile-verification

index 014430041a19feb9fe8faf1d91c87dbae220684b..d88a0678927fec71cd7e48dd0106c0bd5a315f83 100644 (file)
@@ -15,6 +15,8 @@
 #include <string.h>
 #include <sys/wait.h>
 #include <unistd.h>
+
+#include <algorithm>
 #include <iostream>
 #include <string>
 #include <vector>
@@ -74,6 +76,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
       std::clog << "inside VerifyGetSigners" << std::endl;
 
    int fd[2];
+   bool const keyIsID = (key.empty() == false && key[0] != '/');
 
    if (pipe(fd) < 0)
       return "Couldn't create pipe";
@@ -82,12 +85,13 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
    if (pid < 0)
       return string("Couldn't spawn new process") + strerror(errno);
    else if (pid == 0)
-      ExecGPGV(outfile, file, 3, fd, key);
+      ExecGPGV(outfile, file, 3, fd, (keyIsID ? "" : key));
    close(fd[1]);
 
    FILE *pipein = fdopen(fd[0], "r");
 
    // Loop over the output of apt-key (which really is gnupg), and check the signatures.
+   std::vector<std::string> ValidSigners;
    size_t buffersize = 0;
    char *buffer = NULL;
    while (1)
@@ -107,32 +111,31 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
             std::clog << "Got BADSIG! " << std::endl;
          BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
       }
-
-      if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
+      else if (strncmp(buffer, GNUPGNOPUBKEY, sizeof(GNUPGNOPUBKEY)-1) == 0)
       {
          if (Debug == true)
             std::clog << "Got NO_PUBKEY " << std::endl;
          NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
       }
-      if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
+      else if (strncmp(buffer, GNUPGNODATA, sizeof(GNUPGBADSIG)-1) == 0)
       {
          if (Debug == true)
             std::clog << "Got NODATA! " << std::endl;
          BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
       }
-      if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
+      else if (strncmp(buffer, GNUPGKEYEXPIRED, sizeof(GNUPGKEYEXPIRED)-1) == 0)
       {
          if (Debug == true)
             std::clog << "Got KEYEXPIRED! " << std::endl;
          WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
       }
-      if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
+      else if (strncmp(buffer, GNUPGREVKEYSIG, sizeof(GNUPGREVKEYSIG)-1) == 0)
       {
          if (Debug == true)
             std::clog << "Got REVKEYSIG! " << std::endl;
          WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
       }
-      if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
+      else if (strncmp(buffer, GNUPGGOODSIG, sizeof(GNUPGGOODSIG)-1) == 0)
       {
          char *sig = buffer + sizeof(GNUPGPREFIX);
          char *p = sig + sizeof("GOODSIG");
@@ -143,10 +146,48 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
             std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
          GoodSigners.push_back(string(sig));
       }
+      else if (strncmp(buffer, GNUPGVALIDSIG, sizeof(GNUPGVALIDSIG)-1) == 0)
+      {
+         char *sig = buffer + sizeof(GNUPGVALIDSIG);
+         char *p = sig;
+         while (*p && isxdigit(*p))
+            p++;
+         *p = 0;
+         if (Debug == true)
+            std::clog << "Got VALIDSIG, key ID: " << sig << std::endl;
+         ValidSigners.push_back(string(sig));
+      }
    }
    fclose(pipein);
    free(buffer);
 
+   // apt-key has a --keyid parameter, but this requires gpg, so we call it without it
+   // and instead check after the fact which keyids where used for verification
+   if (keyIsID == true)
+   {
+      if (Debug == true)
+        std::clog << "GoodSigs needs to be limited to keyid " << key << std::endl;
+      std::vector<std::string>::iterator const foundItr = std::find(ValidSigners.begin(), ValidSigners.end(), key);
+      bool const found = (foundItr != ValidSigners.end());
+      std::copy(GoodSigners.begin(), GoodSigners.end(), std::back_insert_iterator<std::vector<std::string> >(NoPubKeySigners));
+      if (found)
+      {
+        // we look for GOODSIG here as well as an expired sig is a valid sig as well (but not a good one)
+        std::string const goodlongkeyid = "GOODSIG " + key.substr(24, 16);
+        bool const foundGood = std::find(GoodSigners.begin(), GoodSigners.end(), goodlongkeyid) != GoodSigners.end();
+        if (Debug == true)
+           std::clog << "Key " << key << " is valid sig, is " << goodlongkeyid << " also a good one? " << (foundGood ? "yes" : "no") << std::endl;
+        GoodSigners.clear();
+        if (foundGood)
+        {
+           GoodSigners.push_back(goodlongkeyid);
+           NoPubKeySigners.erase(std::remove(NoPubKeySigners.begin(), NoPubKeySigners.end(), goodlongkeyid), NoPubKeySigners.end());
+        }
+      }
+      else
+        GoodSigners.clear();
+   }
+
    int status;
    waitpid(pid, &status, 0);
    if (Debug == true)
@@ -156,8 +197,18 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
    
    if (WEXITSTATUS(status) == 0)
    {
-      if (GoodSigners.empty())
-         return _("Internal error: Good signature, but could not determine key fingerprint?!");
+      if (keyIsID)
+      {
+        // gpgv will report success, but we want to enforce a certain keyring
+        // so if we haven't found the key the valid we found is in fact invalid
+        if (GoodSigners.empty())
+           return _("At least one invalid signature was encountered.");
+      }
+      else
+      {
+        if (GoodSigners.empty())
+           return _("Internal error: Good signature, but could not determine key fingerprint?!");
+      }
       return "";
    }
    else if (WEXITSTATUS(status) == 1)
index 1226e7dc4c0046d9feede40ba5d0e44715f4ef8c..a1a0d883db6af1c71f6d77521fc3532a08c2bb71 100755 (executable)
@@ -204,6 +204,7 @@ gpg:              unchanged: 1' aptkey --fakeroot update
                testfailure --nomsg aptkey --quiet --readonly --keyring keys/does-not-exist.pub verify signature.gpg signature
                testfailure test -e keys/does-not-exist.pub
 
+               # note: this isn't how apts gpgv method implements keyid for verify
                msgtest 'Test verify a file' 'with good keyid'
                testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
 
index 1c3953c8b38302c5748471984689628fbc9f95b0..759242514b90a605f37e1ac75e1432108574fa1b 100755 (executable)
@@ -92,7 +92,7 @@ touch aptarchive/apt.deb
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 
 updatewithwarnings() {
-       testwarning aptget update
+       testwarning aptget update -o Debug::pkgAcquire::Worker=1 -o Debug::Acquire::gpgv=1
        testsuccess grep -E "$1" rootdir/tmp/testwarning.output
 }
 
@@ -225,7 +225,7 @@ runtest() {
        signreleasefiles 'Joe Sixpack'
        find aptarchive/ -name "$DELETEFILE" -delete
        msgmsg 'Cold archive signed by bad keyid' 'Joe Sixpack'
-       updatewithwarnings '^W: .* NO_PUBKEY'
+       updatewithwarnings '^W: .* be verified because the public key is not available: .*'
 
        sed -i "s#^\(deb\(-src\)\?\) \[signed-by=$MARVIN\] #\1 #" rootdir/etc/apt/sources.list.d/*
 }