Explorar o código

implement Signed-By without using gpg for verification

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
David Kalnischkies %!s(int64=11) %!d(string=hai) anos
pai
achega
4e03c47de1

+ 60 - 9
methods/gpgv.cc

@@ -15,6 +15,8 @@
 #include <string.h>
 #include <string.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <unistd.h>
+
+#include <algorithm>
 #include <iostream>
 #include <iostream>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
@@ -74,6 +76,7 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
       std::clog << "inside VerifyGetSigners" << std::endl;
       std::clog << "inside VerifyGetSigners" << std::endl;
 
 
    int fd[2];
    int fd[2];
+   bool const keyIsID = (key.empty() == false && key[0] != '/');
 
 
    if (pipe(fd) < 0)
    if (pipe(fd) < 0)
       return "Couldn't create pipe";
       return "Couldn't create pipe";
@@ -82,12 +85,13 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
    if (pid < 0)
    if (pid < 0)
       return string("Couldn't spawn new process") + strerror(errno);
       return string("Couldn't spawn new process") + strerror(errno);
    else if (pid == 0)
    else if (pid == 0)
-      ExecGPGV(outfile, file, 3, fd, key);
+      ExecGPGV(outfile, file, 3, fd, (keyIsID ? "" : key));
    close(fd[1]);
    close(fd[1]);
 
 
    FILE *pipein = fdopen(fd[0], "r");
    FILE *pipein = fdopen(fd[0], "r");
 
 
    // Loop over the output of apt-key (which really is gnupg), and check the signatures.
    // Loop over the output of apt-key (which really is gnupg), and check the signatures.
+   std::vector<std::string> ValidSigners;
    size_t buffersize = 0;
    size_t buffersize = 0;
    char *buffer = NULL;
    char *buffer = NULL;
    while (1)
    while (1)
@@ -107,32 +111,31 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
             std::clog << "Got BADSIG! " << std::endl;
             std::clog << "Got BADSIG! " << std::endl;
          BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
          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)
          if (Debug == true)
             std::clog << "Got NO_PUBKEY " << std::endl;
             std::clog << "Got NO_PUBKEY " << std::endl;
          NoPubKeySigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
          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)
          if (Debug == true)
             std::clog << "Got NODATA! " << std::endl;
             std::clog << "Got NODATA! " << std::endl;
          BadSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
          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)
          if (Debug == true)
             std::clog << "Got KEYEXPIRED! " << std::endl;
             std::clog << "Got KEYEXPIRED! " << std::endl;
          WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
          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)
          if (Debug == true)
             std::clog << "Got REVKEYSIG! " << std::endl;
             std::clog << "Got REVKEYSIG! " << std::endl;
          WorthlessSigners.push_back(string(buffer+sizeof(GNUPGPREFIX)));
          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 *sig = buffer + sizeof(GNUPGPREFIX);
          char *p = sig + sizeof("GOODSIG");
          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;
             std::clog << "Got GOODSIG, key ID:" << sig << std::endl;
          GoodSigners.push_back(string(sig));
          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);
    fclose(pipein);
    free(buffer);
    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;
    int status;
    waitpid(pid, &status, 0);
    waitpid(pid, &status, 0);
    if (Debug == true)
    if (Debug == true)
@@ -156,8 +197,18 @@ string GPGVMethod::VerifyGetSigners(const char *file, const char *outfile,
    
    
    if (WEXITSTATUS(status) == 0)
    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 "";
       return "";
    }
    }
    else if (WEXITSTATUS(status) == 1)
    else if (WEXITSTATUS(status) == 1)

+ 1 - 0
test/integration/test-apt-key

@@ -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 --nomsg aptkey --quiet --readonly --keyring keys/does-not-exist.pub verify signature.gpg signature
 		testfailure test -e keys/does-not-exist.pub
 		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'
 		msgtest 'Test verify a file' 'with good keyid'
 		testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
 		testsuccess --nomsg aptkey --quiet --readonly --keyid 'Paranoid' verify signature.gpg signature
 
 

+ 2 - 2
test/integration/test-releasefile-verification

@@ -92,7 +92,7 @@ touch aptarchive/apt.deb
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 PKGFILE="${TESTDIR}/$(echo "$(basename $0)" | sed 's#^test-#Packages-#')"
 
 
 updatewithwarnings() {
 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
 	testsuccess grep -E "$1" rootdir/tmp/testwarning.output
 }
 }
 
 
@@ -225,7 +225,7 @@ runtest() {
 	signreleasefiles 'Joe Sixpack'
 	signreleasefiles 'Joe Sixpack'
 	find aptarchive/ -name "$DELETEFILE" -delete
 	find aptarchive/ -name "$DELETEFILE" -delete
 	msgmsg 'Cold archive signed by bad keyid' 'Joe Sixpack'
 	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/*
 	sed -i "s#^\(deb\(-src\)\?\) \[signed-by=$MARVIN\] #\1 #" rootdir/etc/apt/sources.list.d/*
 }
 }