Imported Upstream version 17.2.0
[platform/upstream/libzypp.git] / zypp / KeyRing.cc
index d6b8246..f999239 100644 (file)
 #include "zypp/KeyRing.h"
 #include "zypp/ExternalProgram.h"
 #include "zypp/TmpPath.h"
+#include "zypp/ZYppCallbacks.h"       // JobReport::instance
+#include "zypp/KeyManager.h"
 
 using std::endl;
 
 #undef  ZYPP_BASE_LOGGER_LOGGROUP
 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::KeyRing"
 
-/** \todo Fix duplicate define in PublicKey/KeyRing */
-#define GPG_BINARY "/usr/bin/gpg2"
-
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
@@ -89,18 +88,24 @@ namespace zypp
     ///   const std::list<PublicKeyData> & cachedPublicKeyData( const Pathname & keyring );
     /// \endcode
     ///////////////////////////////////////////////////////////////////
-    struct CachedPublicKeyData // : private base::NonCopyable - but KeyRing uses RWCOW though also NonCopyable :(
+    struct CachedPublicKeyData : private base::NonCopyable
     {
       const std::list<PublicKeyData> & operator()( const Pathname & keyring_r ) const
       { return getData( keyring_r ); }
 
+      void setDirty( const Pathname & keyring_r )
+      { _cacheMap[keyring_r].setDirty(); }
+
     private:
       struct Cache
       {
-       // Empty copy ctor to allow insert into std::map as
-       // scoped_ptr is noncopyable.
        Cache() {}
-       Cache( const Cache & rhs ) {}
+
+       void setDirty()
+       {
+         _keyringK.reset();
+         _keyringP.reset();
+       }
 
        void assertCache( const Pathname & keyring_r )
        {
@@ -137,38 +142,17 @@ namespace zypp
 
       const std::list<PublicKeyData> & getData( const Pathname & keyring_r, Cache & cache_r ) const
       {
-       if ( cache_r.hasChanged() )
-       {
-         const char* argv[] =
-         {
-           GPG_BINARY,
-           "--list-public-keys",
-           "--homedir", keyring_r.c_str(),
-           "--no-default-keyring",
-           "--quiet",
-           "--with-colons",
-           "--fixed-list-mode",
-           "--with-fingerprint",
-           "--with-sig-list",
-           "--no-tty",
-           "--no-greeting",
-           "--batch",
-           "--status-fd", "1",
-           NULL
-         };
-
-         PublicKeyScanner scanner;
-         ExternalProgram prog( argv ,ExternalProgram::Discard_Stderr, false, -1, true );
-         for( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-         {
-           scanner.scan( line );
-         }
-         prog.close();
-
-         cache_r._data.swap( scanner._keys );
-         MIL << "Found keys: " << cache_r._data  << endl;
-       }
-       return cache_r._data;
+        if ( cache_r.hasChanged() ) {
+          shared_ptr<KeyManagerCtx> ctx = KeyManagerCtx::createForOpenPGP();
+          if (ctx) {
+            if (ctx->setHomedir(keyring_r)) {
+              std::list<PublicKeyData> foundKeys = ctx->listKeys();
+              cache_r._data.swap(foundKeys);
+            }
+          }
+          MIL << "Found keys: " << cache_r._data  << endl;
+        }
+        return cache_r._data;
       }
 
       mutable CacheMap _cacheMap;
@@ -233,6 +217,8 @@ namespace zypp
 
     PublicKey exportKey( const std::string & id, const Pathname & keyring );
     PublicKey exportKey( const PublicKeyData & keyData, const Pathname & keyring );
+    PublicKey exportKey( const PublicKey & key, const Pathname & keyring )
+    { return exportKey( key.keyData(), keyring ); }
 
     void dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream );
     filesystem::TmpFile dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring );
@@ -266,18 +252,51 @@ namespace zypp
   };
   ///////////////////////////////////////////////////////////////////
 
+  namespace
+  {
+    /// Handle signal emission from within KeyRing::Impl::importKey
+    struct ImportKeyCBHelper
+    {
+      void operator()( const PublicKey & key_r )
+      {
+       try {
+         _rpmdbEmitSignal->trustedKeyAdded( key_r );
+         _emitSignal->trustedKeyAdded( key_r );
+       }
+       catch ( const Exception & excp )
+       {
+         ERR << "Could not import key into rpmdb: " << excp << endl;
+         // TODO: JobReport as hotfix for bsc#1057188; should bubble up and go through some callback
+         JobReport::error( excp.asUserHistory() );
+       }
+      }
+
+    private:
+      callback::SendReport<target::rpm::KeyRingSignals> _rpmdbEmitSignal;
+      callback::SendReport<KeyRingSignals>              _emitSignal;
+    };
+  } // namespace
+
 
   void KeyRing::Impl::importKey( const PublicKey & key, bool trusted )
   {
     importKey( key.path(), trusted ? trustedKeyRing() : generalKeyRing() );
+    MIL << "Imported key " << key << " to " << (trusted ? "trustedKeyRing" : "generalKeyRing" ) << endl;
 
     if ( trusted )
     {
-      callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
-      callback::SendReport<KeyRingSignals> emitSignal;
-
-      rpmdbEmitSignal->trustedKeyAdded( key );
-      emitSignal->trustedKeyAdded( key );
+      ImportKeyCBHelper emitSignal;
+      if ( key.hiddenKeys().empty() )
+      {
+       emitSignal( key );
+      }
+      else
+      {
+       // multiple keys: Export individual keys ascii armored to import in rpmdb
+       emitSignal( exportKey( key, trustedKeyRing() ) );
+       for ( const PublicKeyData & hkey : key.hiddenKeys() )
+         emitSignal( exportKey( hkey, trustedKeyRing() ) );
+      }
     }
   }
 
@@ -288,37 +307,46 @@ namespace zypp
 
   void KeyRing::Impl::deleteKey( const std::string & id, bool trusted )
   {
-    PublicKey key;
-
-    if ( trusted )
+    PublicKeyData keyDataToDel( publicKeyExists( id, trusted ? trustedKeyRing() : generalKeyRing() ) );
+    if ( ! keyDataToDel )
     {
-       key = exportKey( id, trustedKeyRing() );
+      WAR << "Key to delete [" << id << "] is not in " << (trusted ? "trustedKeyRing" : "generalKeyRing" ) << endl;
+      return;
     }
-
     deleteKey( id, trusted ? trustedKeyRing() : generalKeyRing() );
+    MIL << "Deleted key [" << id << "] from " << (trusted ? "trustedKeyRing" : "generalKeyRing" ) << endl;
 
     if ( trusted )
-    {
-      callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
-      callback::SendReport<KeyRingSignals> emitSignal;
+    try {
+      PublicKey key( keyDataToDel );
 
+      callback::SendReport<target::rpm::KeyRingSignals> rpmdbEmitSignal;
       rpmdbEmitSignal->trustedKeyRemoved( key );
+
+      callback::SendReport<KeyRingSignals> emitSignal;
       emitSignal->trustedKeyRemoved( key );
     }
+    catch ( const Exception & excp )
+    {
+      ERR << "Could not delete key from rpmmdb: " << excp << endl;
+      // TODO: JobReport as hotfix for bsc#1057188; should bubble up and go through some callback
+      JobReport::error( excp.asUserHistory() );
+    }
   }
 
   PublicKeyData KeyRing::Impl::publicKeyExists( const std::string & id, const Pathname & keyring )
   {
-    MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
-    const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
-    for_( it, keys.begin(), keys.end() )
+    PublicKeyData ret;
+    for ( const PublicKeyData & key : publicKeyData( keyring ) )
     {
-      if ( id == (*it).id() )
+      if ( key.providesKey( id ) )
       {
-        return *it;
+       ret = key;
+       break;
       }
     }
-    return PublicKeyData();
+    MIL << (ret ? "Found" : "No") << " key [" << id << "] in keyring " << keyring << endl;
+    return ret;
   }
 
   PublicKey KeyRing::Impl::exportKey( const PublicKeyData & keyData, const Pathname & keyring )
@@ -333,40 +361,23 @@ namespace zypp
       return PublicKey( dumpPublicKeyToTmp( keyData.id(), keyring ), keyData );
 
     // Here: key not found
-    WAR << "No key " << id << " to export from " << keyring << endl;
+    WAR << "No key [" << id << "] to export from " << keyring << endl;
     return PublicKey();
   }
 
 
   void KeyRing::Impl::dumpPublicKey( const std::string & id, const Pathname & keyring, std::ostream & stream )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "-a",
-      "--export",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--no-greeting",
-      "--no-permission-warning",
-      "--batch",
-      id.c_str(),
-      NULL
-    };
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-    for ( std::string line = prog.receiveLine(); !line.empty(); line = prog.receiveLine() )
-    {
-      stream << line;
-    }
-    prog.close();
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if (!ctx || !ctx->setHomedir(keyring))
+      return;
+    ctx->exportKey(id, stream);
   }
 
   filesystem::TmpFile KeyRing::Impl::dumpPublicKeyToTmp( const std::string & id, const Pathname & keyring )
   {
     filesystem::TmpFile tmpFile( _base_dir, "pubkey-"+id+"-" );
-    MIL << "Going to export key " << id << " from " << keyring << " to " << tmpFile.path() << endl;
+    MIL << "Going to export key [" << id << "] from " << keyring << " to " << tmpFile.path() << endl;
 
     std::ofstream os( tmpFile.path().c_str() );
     dumpPublicKey( id, keyring, os );
@@ -385,14 +396,14 @@ namespace zypp
     if( signature.empty() || (!PathInfo( signature ).isExist()) )
     {
       bool res = report->askUserToAcceptUnsignedFile( filedesc, context );
-      MIL << "User decision on unsigned file: " << res << endl;
+      MIL << "askUserToAcceptUnsignedFile: " << res << endl;
       return res;
     }
 
-    // get the id of the signature
+    // get the id of the signature (it might be a subkey id!)
     std::string id = readSignatureKeyId( signature );
 
-    // doeskey exists in trusted keyring
+    // does key exists in trusted keyring
     PublicKeyData trustedKeyData( publicKeyExists( id, trustedKeyRing() ) );
     if ( trustedKeyData )
     {
@@ -405,27 +416,32 @@ namespace zypp
       {
         // bnc #393160: Comment #30: Compare at least the fingerprint
         // in case an attacker created a key the the same id.
+       //
+       // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
+       // to compare the subkey sets, to tell whether a key was updated.
+       // because created() remains unchanged if the primary key is not touched.
+       // For now we wait until a new subkey signs the data and treat it as a
+       //  new key (else part below).
         if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
           && trustedKeyData.created() < generalKeyData.created() )
         {
           MIL << "Key was updated. Saving new version into trusted keyring: " << generalKeyData << endl;
           importKey( exportKey( generalKeyData, generalKeyRing() ), true );
-         trustedKeyData = generalKeyData = PublicKeyData(); // invalidated by import.
+         trustedKeyData = publicKeyExists( id, trustedKeyRing() ); // re-read: invalidated by import?
        }
       }
 
-      if ( ! trustedKeyData )  // invalidated by previous import
-       trustedKeyData = publicKeyExists( id, trustedKeyRing() );
+      // it exists, is trusted, does it validate?
       report->infoVerify( filedesc, trustedKeyData, context );
-
-      // it exists, is trusted, does it validates?
       if ( verifyFile( file, signature, trustedKeyRing() ) )
       {
         return (sigValid_r=true);      // signature is actually successfully validated!
       }
       else
       {
-        return report->askUserToAcceptVerificationFailed( filedesc, exportKey( trustedKeyData, trustedKeyRing() ), context );
+       bool res = report->askUserToAcceptVerificationFailed( filedesc, exportKey( trustedKeyData, trustedKeyRing() ), context );
+       MIL << "askUserToAcceptVerificationFailed: " << res << endl;
+        return res;
       }
     }
     else
@@ -434,68 +450,51 @@ namespace zypp
       if ( generalKeyData )
       {
         PublicKey key( exportKey( generalKeyData, generalKeyRing() ) );
-        MIL << "Exported key " << id << " to " << key.path() << endl;
-        MIL << "Key " << id << " " << key.name() << " is not trusted" << endl;
+        MIL << "Key [" << id << "] " << key.name() << " is not trusted" << endl;
 
         // ok the key is not trusted, ask the user to trust it or not
         KeyRingReport::KeyTrust reply = report->askUserToAcceptKey( key, context );
         if ( reply == KeyRingReport::KEY_TRUST_TEMPORARILY ||
             reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
         {
-          MIL << "User wants to trust key " << id << " " << key.name() << endl;
-          //dumpFile( unKey.path() );
+          MIL << "User wants to trust key [" << id << "] " << key.name() << endl;
 
           Pathname whichKeyring;
           if ( reply == KeyRingReport::KEY_TRUST_AND_IMPORT )
           {
-            MIL << "User wants to import key " << id << " " << key.name() << endl;
+            MIL << "User wants to import key [" << id << "] " << key.name() << endl;
             importKey( key, true );
             whichKeyring = trustedKeyRing();
           }
           else
             whichKeyring = generalKeyRing();
 
-          // emit key added
+          // does it validate?
+         report->infoVerify( filedesc, generalKeyData, context );
           if ( verifyFile( file, signature, whichKeyring ) )
           {
-            MIL << "File signature is verified" << endl;
            return (sigValid_r=true);   // signature is actually successfully validated!
           }
           else
           {
-            MIL << "File signature check fails" << endl;
-            if ( report->askUserToAcceptVerificationFailed( filedesc, key, context ) )
-            {
-              MIL << "User continues anyway." << endl;
-              return true;
-            }
-            else
-            {
-              MIL << "User does not want to continue" << endl;
-              return false;
-            }
+           bool res = report->askUserToAcceptVerificationFailed( filedesc, key, context );
+           MIL << "askUserToAcceptVerificationFailed: " << res << endl;
+           return res;
           }
         }
         else
         {
-          MIL << "User does not want to trust key " << id << " " << key.name() << endl;
+          MIL << "User does not want to trust key [" << id << "] " << key.name() << endl;
           return false;
         }
       }
       else
       {
-        // unknown key...
+        // signed with an unknown key...
         MIL << "File [" << file << "] ( " << filedesc << " ) signed with unknown key [" << id << "]" << endl;
-        if ( report->askUserToAcceptUnknownKey( filedesc, id, context ) )
-        {
-          MIL << "User wants to accept unknown key " << id << endl;
-          return true;
-        }
-        else
-        {
-          MIL << "User does not want to accept unknown key " << id << endl;
-          return false;
-        }
+       bool res = report->askUserToAcceptUnknownKey( filedesc, id, context );
+       MIL << "askUserToAcceptUnknownKey: " << res << endl;
+       return res;
       }
     }
     return false;
@@ -523,139 +522,61 @@ namespace zypp
                                   % keyfile.asString()
                                   % keyring.asString() ));
 
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--import",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--no-greeting",
-      "--no-permission-warning",
-      "--status-fd", "1",
-      keyfile.asString().c_str(),
-      NULL
-    };
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx || !ctx->setHomedir(keyring))
+      ZYPP_THROW(KeyRingException(_("Failed to import key.")));
 
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-    prog.close();
+    cachedPublicKeyData.setDirty( keyring );
+    if(!ctx->importKey(keyfile))
+      ZYPP_THROW(KeyRingException(_("Failed to import key.")));
   }
 
   void KeyRing::Impl::deleteKey( const std::string & id, const Pathname & keyring )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--delete-keys",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--yes",
-      "--quiet",
-      "--no-tty",
-      "--batch",
-      "--status-fd", "1",
-      id.c_str(),
-      NULL
-    };
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx) {
+      ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
+
+    if(!ctx->setHomedir(keyring)) {
+      ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
 
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
+    if(!ctx->deleteKey(id)){
+      ZYPP_THROW(KeyRingException(_("Failed to delete key.")));
+    }
 
-    int code = prog.close();
-    if ( code )
-      ZYPP_THROW(Exception(_("Failed to delete key.")));
-    else
-      MIL << "Deleted key " << id << " from keyring " << keyring << endl;
+    cachedPublicKeyData.setDirty( keyring );
   }
 
-
   std::string KeyRing::Impl::readSignatureKeyId( const Pathname & signature )
   {
     if ( ! PathInfo( signature ).isFile() )
-      ZYPP_THROW(Exception( str::Format(_("Signature file %s not found")) % signature.asString() ));
+      ZYPP_THROW(KeyRingException( str::Format(_("Signature file %s not found")) % signature.asString() ));
 
-    MIL << "Determining key id if signature " << signature << endl;
-    // HACK create a tmp keyring with no keys
-    filesystem::TmpDir dir( _base_dir, "fake-keyring" );
-    std::string tmppath( dir.path().asString() );
+    MIL << "Determining key id of signature " << signature << endl;
 
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--homedir", tmppath.c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--no-greeting",
-      "--batch",
-      "--status-fd", "1",
-      signature.asString().c_str(),
-      NULL
-    };
-
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
-
-    std::string line;
-    int count = 0;
-
-    str::regex rxNoKey( "^\\[GNUPG:\\] NO_PUBKEY (.+)\n$" );
-    std::string id;
-    for( line = prog.receiveLine(), count=0; !line.empty(); line = prog.receiveLine(), count++ )
-    {
-      //MIL << "[" << line << "]" << endl;
-      str::smatch what;
-      if( str::regex_match( line, what, rxNoKey ) )
-      {
-        if ( what.size() >= 1 )
-       {
-          id = what[1];
-         break;
-       }
-        //dumpRegexpResults( what );
-      }
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if(!ctx) {
+      return std::string();
     }
 
-    if ( count == 0 )
-    {
-      MIL << "no output" << endl;
+    std::list<std::string> fprs = ctx->readSignatureFingerprints(signature);
+    if (fprs.size()) {
+      std::string &id = fprs.back();
+      MIL << "Determined key id [" << id << "] for signature " << signature << endl;
+      return id;
     }
-
-    MIL << "Determined key id [" << id << "] for signature " << signature << endl;
-    prog.close();
-    return id;
+    return std::string();
   }
 
   bool KeyRing::Impl::verifyFile( const Pathname & file, const Pathname & signature, const Pathname & keyring )
   {
-    const char* argv[] =
-    {
-      GPG_BINARY,
-      "--verify",
-      "--homedir", keyring.asString().c_str(),
-      "--no-default-keyring",
-      "--quiet",
-      "--no-tty",
-      "--batch",
-      "--no-greeting",
-      "--status-fd", "1",
-      signature.asString().c_str(),
-      file.asString().c_str(),
-      NULL
-    };
-
-    // no need to parse output for now
-    //     [GNUPG:] SIG_ID yCc4u223XRJnLnVAIllvYbUd8mQ 2006-03-29 1143618744
-    //     [GNUPG:] GOODSIG A84EDAE89C800ACA SuSE Package Signing Key <build@suse.de>
-    //     gpg: Good signature from "SuSE Package Signing Key <build@suse.de>"
-    //     [GNUPG:] VALIDSIG 79C179B2E1C820C1890F9994A84EDAE89C800ACA 2006-03-29 1143618744 0 3 0 17 2 00 79C179B2E1C820C1890F9994A84EDAE89C800ACA
-    //     [GNUPG:] TRUST_UNDEFINED
-
-    //     [GNUPG:] ERRSIG A84EDAE89C800ACA 17 2 00 1143618744 9
-    //     [GNUPG:] NO_PUBKEY A84EDAE89C800ACA
-
-    ExternalProgram prog( argv,ExternalProgram::Discard_Stderr, false, -1, true );
+    KeyManagerCtx::Ptr ctx = KeyManagerCtx::createForOpenPGP();
+    if (!ctx || !ctx->setHomedir(keyring))
+      return false;
 
-    return ( prog.close() == 0 ) ? true : false;
+    return ctx->verify(file, signature);
   }
 
   ///////////////////////////////////////////////////////////////////