File indexing completed on 2025-01-12 04:19:46
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