File indexing completed on 2024-05-05 04:45:05

0001 /*
0002  * Copyright (C) 2003-2008  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
0017  */
0018 
0019 #include "mykeystorelist.h"
0020 #include "mypgpkeycontext.h"
0021 #include "utils.h"
0022 #include <QFileInfo>
0023 #include <QMutexLocker>
0024 
0025 using namespace QCA;
0026 
0027 namespace gpgQCAPlugin {
0028 
0029 Q_GLOBAL_STATIC(QMutex, ksl_mutex)
0030 
0031 static MyKeyStoreList *keyStoreList = nullptr;
0032 MyKeyStoreList::MyKeyStoreList(Provider *p)
0033     : KeyStoreListContext(p)
0034     , initialized(false)
0035     , gpg(find_bin(), this)
0036     , pubdirty(false)
0037     , secdirty(false)
0038     , ringWatch(this)
0039 {
0040     QMutexLocker locker(ksl_mutex());
0041     keyStoreList = this;
0042 
0043     connect(&gpg, &GpgOp::finished, this, &MyKeyStoreList::gpg_finished);
0044     connect(&ringWatch, &RingWatch::changed, this, &MyKeyStoreList::ring_changed);
0045 }
0046 
0047 MyKeyStoreList::~MyKeyStoreList()
0048 {
0049     QMutexLocker locker(ksl_mutex());
0050     keyStoreList = nullptr;
0051 }
0052 
0053 Provider::Context *MyKeyStoreList::clone() const
0054 {
0055     return nullptr;
0056 }
0057 
0058 QString MyKeyStoreList::name(int) const
0059 {
0060     return QStringLiteral("GnuPG Keyring");
0061 }
0062 
0063 KeyStore::Type MyKeyStoreList::type(int) const
0064 {
0065     return KeyStore::PGPKeyring;
0066 }
0067 
0068 QString MyKeyStoreList::storeId(int) const
0069 {
0070     return QStringLiteral("qca-gnupg");
0071 }
0072 
0073 QList<int> MyKeyStoreList::keyStores()
0074 {
0075     // we just support one fixed keyring, if any
0076     QList<int> list;
0077     if (initialized)
0078         list += 0;
0079     return list;
0080 }
0081 
0082 void MyKeyStoreList::start()
0083 {
0084     // kick start our init procedure:
0085     //   ensure gpg is installed
0086     //   obtain keyring file names for monitoring
0087     //   cache initial keyrings
0088 
0089     init_step = 0;
0090     gpg.doCheck();
0091 }
0092 
0093 bool MyKeyStoreList::isReadOnly(int) const
0094 {
0095     return false;
0096 }
0097 
0098 QList<KeyStoreEntry::Type> MyKeyStoreList::entryTypes(int) const
0099 {
0100     QList<KeyStoreEntry::Type> list;
0101     list += KeyStoreEntry::TypePGPSecretKey;
0102     list += KeyStoreEntry::TypePGPPublicKey;
0103     return list;
0104 }
0105 
0106 QList<KeyStoreEntryContext *> MyKeyStoreList::entryList(int)
0107 {
0108     QMutexLocker locker(&ringMutex);
0109 
0110     QList<KeyStoreEntryContext *> out;
0111 
0112     foreach (const GpgOp::Key &pkey, pubkeys) {
0113         PGPKey pub, sec;
0114 
0115         const QString id = pkey.keyItems.first().id;
0116 
0117         MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
0118         // not secret, in keyring
0119         kc->set(pkey, false, true, pkey.isTrusted);
0120         pub.change(kc);
0121 
0122         // optional
0123         sec = getSecKey(id, pkey.userIds);
0124 
0125         MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
0126         c->_storeId        = storeId(0);
0127         c->_storeName      = name(0);
0128         out.append(c);
0129     }
0130 
0131     return out;
0132 }
0133 
0134 KeyStoreEntryContext *MyKeyStoreList::entry(int, const QString &entryId)
0135 {
0136     QMutexLocker locker(&ringMutex);
0137 
0138     PGPKey pub = getPubKey(entryId);
0139     if (pub.isNull())
0140         return nullptr;
0141 
0142     // optional
0143     const PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
0144 
0145     MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
0146     c->_storeId        = storeId(0);
0147     c->_storeName      = name(0);
0148     return c;
0149 }
0150 
0151 KeyStoreEntryContext *MyKeyStoreList::entryPassive(const QString &serialized)
0152 {
0153     QMutexLocker locker(&ringMutex);
0154 
0155     const QStringList parts = serialized.split(QLatin1Char(':'));
0156     if (parts.count() < 2)
0157         return nullptr;
0158     if (unescape_string(parts[0]) != QLatin1String("qca-gnupg-1"))
0159         return nullptr;
0160 
0161     QString entryId = unescape_string(parts[1]);
0162     if (entryId.isEmpty())
0163         return nullptr;
0164 
0165     PGPKey pub = getPubKey(entryId);
0166     if (pub.isNull())
0167         return nullptr;
0168 
0169     // optional
0170     const PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
0171 
0172     MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
0173     c->_storeId        = storeId(0);
0174     c->_storeName      = name(0);
0175     return c;
0176 }
0177 
0178 // TODO: cache should reflect this change immediately
0179 QString MyKeyStoreList::writeEntry(int, const PGPKey &key)
0180 {
0181     const MyPGPKeyContext *kc  = static_cast<const MyPGPKeyContext *>(key.context());
0182     const QByteArray       buf = kc->toBinary();
0183 
0184     GpgOp gpg(find_bin());
0185     gpg.doImport(buf);
0186     gpg_waitForFinished(&gpg);
0187     gpg_keyStoreLog(gpg.readDiagnosticText());
0188     if (!gpg.success())
0189         return QString();
0190 
0191     return kc->_props.keyId;
0192 }
0193 
0194 // TODO: cache should reflect this change immediately
0195 bool MyKeyStoreList::removeEntry(int, const QString &entryId)
0196 {
0197     ringMutex.lock();
0198     PGPKey pub = getPubKey(entryId);
0199     ringMutex.unlock();
0200 
0201     const MyPGPKeyContext *kc          = static_cast<const MyPGPKeyContext *>(pub.context());
0202     QString                fingerprint = kc->_props.fingerprint;
0203 
0204     GpgOp gpg(find_bin());
0205     gpg.doDeleteKey(fingerprint);
0206     gpg_waitForFinished(&gpg);
0207     gpg_keyStoreLog(gpg.readDiagnosticText());
0208     return gpg.success();
0209 }
0210 
0211 MyKeyStoreList *MyKeyStoreList::instance()
0212 {
0213     QMutexLocker locker(ksl_mutex());
0214     return keyStoreList;
0215 }
0216 
0217 void MyKeyStoreList::ext_keyStoreLog(const QString &str)
0218 {
0219     if (str.isEmpty())
0220         return;
0221 
0222     // FIXME: collect and emit in one pass
0223     QMetaObject::invokeMethod(this, "diagnosticText", Qt::QueuedConnection, Q_ARG(QString, str));
0224 }
0225 
0226 PGPKey MyKeyStoreList::getPubKey(const QString &keyId) const
0227 {
0228     int at = -1;
0229     for (int n = 0; n < pubkeys.count(); ++n) {
0230         if (pubkeys[n].keyItems.first().id == keyId) {
0231             at = n;
0232             break;
0233         }
0234     }
0235     if (at == -1)
0236         return PGPKey();
0237 
0238     const GpgOp::Key &pkey = pubkeys[at];
0239 
0240     PGPKey           pub;
0241     MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
0242     // not secret, in keyring
0243     kc->set(pkey, false, true, pkey.isTrusted);
0244     pub.change(kc);
0245 
0246     return pub;
0247 }
0248 
0249 PGPKey MyKeyStoreList::getSecKey(const QString &keyId, const QStringList &userIdsOverride) const
0250 {
0251     Q_UNUSED(userIdsOverride);
0252 
0253     int at = -1;
0254     for (int n = 0; n < seckeys.count(); ++n) {
0255         if (seckeys[n].keyItems.first().id == keyId) {
0256             at = n;
0257             break;
0258         }
0259     }
0260     if (at == -1)
0261         return PGPKey();
0262 
0263     const GpgOp::Key &skey = seckeys[at];
0264 
0265     PGPKey           sec;
0266     MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
0267     // secret, in keyring, trusted
0268     kc->set(skey, true, true, true);
0269     // kc->_props.userIds = userIdsOverride;
0270     sec.change(kc);
0271 
0272     return sec;
0273 }
0274 
0275 PGPKey MyKeyStoreList::publicKeyFromId(const QString &keyId)
0276 {
0277     QMutexLocker locker(&ringMutex);
0278 
0279     int at = -1;
0280     for (int n = 0; n < pubkeys.count(); ++n) {
0281         const GpgOp::Key &pkey = pubkeys[n];
0282         for (int k = 0; k < pkey.keyItems.count(); ++k) {
0283             const GpgOp::KeyItem &ki = pkey.keyItems[k];
0284             if (ki.id == keyId) {
0285                 at = n;
0286                 break;
0287             }
0288         }
0289         if (at != -1)
0290             break;
0291     }
0292     if (at == -1)
0293         return PGPKey();
0294 
0295     const GpgOp::Key &pkey = pubkeys[at];
0296 
0297     PGPKey           pub;
0298     MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
0299     // not secret, in keyring
0300     kc->set(pkey, false, true, pkey.isTrusted);
0301     pub.change(kc);
0302 
0303     return pub;
0304 }
0305 
0306 PGPKey MyKeyStoreList::secretKeyFromId(const QString &keyId)
0307 {
0308     QMutexLocker locker(&ringMutex);
0309 
0310     int at = -1;
0311     for (int n = 0; n < seckeys.count(); ++n) {
0312         const GpgOp::Key &skey = seckeys[n];
0313         for (int k = 0; k < skey.keyItems.count(); ++k) {
0314             const GpgOp::KeyItem &ki = skey.keyItems[k];
0315             if (ki.id == keyId) {
0316                 at = n;
0317                 break;
0318             }
0319         }
0320         if (at != -1)
0321             break;
0322     }
0323     if (at == -1)
0324         return PGPKey();
0325 
0326     const GpgOp::Key &skey = seckeys[at];
0327 
0328     PGPKey           sec;
0329     MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
0330     // secret, in keyring, trusted
0331     kc->set(skey, true, true, true);
0332     sec.change(kc);
0333 
0334     return sec;
0335 }
0336 
0337 void MyKeyStoreList::gpg_finished()
0338 {
0339     gpg_keyStoreLog(gpg.readDiagnosticText());
0340 
0341     if (!initialized) {
0342         // any steps that fail during init, just give up completely
0343         if (!gpg.success()) {
0344             ringWatch.clear();
0345             emit busyEnd();
0346             return;
0347         }
0348 
0349         // check
0350         if (init_step == 0) {
0351             // obtain keyring file names for monitoring
0352             init_step = 1;
0353             homeDir   = gpg.homeDir();
0354             gpg.doSecretKeyringFile();
0355         }
0356         // secret keyring filename
0357         else if (init_step == 1) {
0358             secring = QFileInfo(gpg.keyringFile()).canonicalFilePath();
0359 
0360             if (secring.isEmpty()) {
0361                 secring = homeDir + QStringLiteral("/secring.gpg");
0362             }
0363             ringWatch.add(secring);
0364 
0365             // obtain keyring file names for monitoring
0366             init_step = 2;
0367             gpg.doPublicKeyringFile();
0368         }
0369         // public keyring filename
0370         else if (init_step == 2) {
0371             pubring = QFileInfo(gpg.keyringFile()).canonicalFilePath();
0372             if (pubring.isEmpty()) {
0373                 pubring = homeDir + QStringLiteral("/pubring.gpg");
0374             }
0375             ringWatch.add(pubring);
0376 
0377             // cache initial keyrings
0378             init_step = 3;
0379             gpg.doSecretKeys();
0380         } else if (init_step == 3) {
0381             ringMutex.lock();
0382             seckeys = gpg.keys();
0383             ringMutex.unlock();
0384 
0385             // cache initial keyrings
0386             init_step = 4;
0387             gpg.doPublicKeys();
0388         } else if (init_step == 4) {
0389             ringMutex.lock();
0390             pubkeys = gpg.keys();
0391             ringMutex.unlock();
0392 
0393             initialized = true;
0394             handleDirtyRings();
0395             emit busyEnd();
0396         }
0397     } else {
0398         if (!gpg.success())
0399             return;
0400 
0401         const GpgOp::Type op = gpg.op();
0402         if (op == GpgOp::SecretKeys) {
0403             ringMutex.lock();
0404             seckeys = gpg.keys();
0405             ringMutex.unlock();
0406 
0407             secdirty = false;
0408         } else if (op == GpgOp::PublicKeys) {
0409             ringMutex.lock();
0410             pubkeys = gpg.keys();
0411             ringMutex.unlock();
0412 
0413             pubdirty = false;
0414         }
0415 
0416         if (!secdirty && !pubdirty) {
0417             emit storeUpdated(0);
0418             return;
0419         }
0420 
0421         handleDirtyRings();
0422     }
0423 }
0424 
0425 void MyKeyStoreList::ring_changed(const QString &filePath)
0426 {
0427     ext_keyStoreLog(QStringLiteral("ring_changed: [%1]\n").arg(filePath));
0428 
0429     if (filePath == secring)
0430         sec_changed();
0431     else if (filePath == pubring)
0432         pub_changed();
0433 }
0434 
0435 void MyKeyStoreList::pub_changed()
0436 {
0437     pubdirty = true;
0438     handleDirtyRings();
0439 }
0440 
0441 void MyKeyStoreList::sec_changed()
0442 {
0443     secdirty = true;
0444     handleDirtyRings();
0445 }
0446 
0447 void MyKeyStoreList::handleDirtyRings()
0448 {
0449     if (!initialized || gpg.isActive())
0450         return;
0451 
0452     if (secdirty)
0453         gpg.doSecretKeys();
0454     else if (pubdirty)
0455         gpg.doPublicKeys();
0456 }
0457 
0458 } // end namespace gpgQCAPlugin