File indexing completed on 2024-09-08 04:18:39
0001 /* 0002 Copyright (C) 2007 Justin Karneges <justin@affinix.com> 0003 0004 Permission is hereby granted, free of charge, to any person obtaining a copy 0005 of this software and associated documentation files (the "Software"), to deal 0006 in the Software without restriction, including without limitation the rights 0007 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0008 copies of the Software, and to permit persons to whom the Software is 0009 furnished to do so, subject to the following conditions: 0010 0011 The above copyright notice and this permission notice shall be included in 0012 all copies or substantial portions of the Software. 0013 0014 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0015 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0016 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0017 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 0018 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 0019 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0020 */ 0021 0022 #include "prompter.h" 0023 0024 #include <QApplication> 0025 #include <QInputDialog> 0026 #include <QMessageBox> 0027 #include <QtCore> 0028 #include <QtCrypto> 0029 #include <QtGui> 0030 0031 class Prompter::Private : public QObject 0032 { 0033 Q_OBJECT 0034 public: 0035 Prompter *q; 0036 0037 class Item 0038 { 0039 public: 0040 int id; 0041 QCA::Event event; 0042 }; 0043 0044 QCA::EventHandler handler; 0045 QList<Item> pending; 0046 bool prompting; 0047 QMessageBox *token_prompt; 0048 bool auto_accept; 0049 0050 QCA::KeyStoreManager ksm; 0051 QList<QCA::KeyStore *> keyStores; 0052 0053 Private(Prompter *_q) 0054 : QObject(_q) 0055 , q(_q) 0056 , handler(this) 0057 , prompting(false) 0058 , token_prompt(0) 0059 , ksm(this) 0060 { 0061 connect(&handler, SIGNAL(eventReady(int, const QCA::Event &)), SLOT(ph_eventReady(int, const QCA::Event &))); 0062 handler.start(); 0063 0064 connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &))); 0065 foreach (const QString &keyStoreId, ksm.keyStores()) 0066 ks_available(keyStoreId); 0067 } 0068 0069 ~Private() 0070 { 0071 qDeleteAll(keyStores); 0072 0073 while (!pending.isEmpty()) 0074 handler.reject(pending.takeFirst().id); 0075 } 0076 0077 private Q_SLOTS: 0078 void ph_eventReady(int id, const QCA::Event &event) 0079 { 0080 Item i; 0081 i.id = id; 0082 i.event = event; 0083 pending += i; 0084 nextEvent(); 0085 } 0086 0087 void nextEvent() 0088 { 0089 if (prompting || pending.isEmpty()) 0090 return; 0091 0092 prompting = true; 0093 0094 const Item &i = pending.first(); 0095 const int &id = i.id; 0096 const QCA::Event &event = i.event; 0097 0098 if (event.type() == QCA::Event::Password) { 0099 QCA::SecureArray known = q->knownPassword(event); 0100 if (!known.isEmpty()) { 0101 handler.submitPassword(id, known); 0102 goto end; 0103 } 0104 0105 QString type = Prompter::tr("password"); 0106 if (event.passwordStyle() == QCA::Event::StylePassphrase) 0107 type = Prompter::tr("passphrase"); 0108 else if (event.passwordStyle() == QCA::Event::StylePIN) 0109 type = Prompter::tr("PIN"); 0110 0111 QString str; 0112 if (event.source() == QCA::Event::KeyStore) { 0113 QString name; 0114 QCA::KeyStoreEntry entry = event.keyStoreEntry(); 0115 if (!entry.isNull()) { 0116 name = entry.name(); 0117 } else { 0118 if (event.keyStoreInfo().type() == QCA::KeyStore::SmartCard) 0119 name = Prompter::tr("the '%1' token").arg(event.keyStoreInfo().name()); 0120 else 0121 name = event.keyStoreInfo().name(); 0122 } 0123 str = Prompter::tr("Enter %1 for %2").arg(type, name); 0124 } else if (!event.fileName().isEmpty()) { 0125 QFileInfo fi(event.fileName()); 0126 str = Prompter::tr("Enter %1 for %2:").arg(type, fi.fileName()); 0127 } else 0128 str = Prompter::tr("Enter %1:").arg(type); 0129 0130 bool ok; 0131 QString pass = QInputDialog::getText(0, 0132 QApplication::instance()->applicationName() + ": " + tr("Prompt"), 0133 str, 0134 QLineEdit::Password, 0135 QString(), 0136 &ok); 0137 if (ok) { 0138 QCA::SecureArray password = pass.toUtf8(); 0139 q->userSubmitted(password, event); 0140 handler.submitPassword(id, password); 0141 } else 0142 handler.reject(id); 0143 } else if (event.type() == QCA::Event::Token) { 0144 // even though we're being prompted for a missing token, 0145 // we should still check if the token is present, due to 0146 // a possible race between insert and token request. 0147 bool found = false; 0148 0149 // token-only 0150 if (event.keyStoreEntry().isNull()) { 0151 foreach (QCA::KeyStore *ks, keyStores) { 0152 if (ks->id() == event.keyStoreInfo().id()) { 0153 found = true; 0154 break; 0155 } 0156 } 0157 } 0158 // token-entry 0159 else { 0160 QCA::KeyStoreEntry kse = event.keyStoreEntry(); 0161 0162 QCA::KeyStore *ks = 0; 0163 foreach (QCA::KeyStore *i, keyStores) { 0164 if (i->id() == event.keyStoreInfo().id()) { 0165 ks = i; 0166 break; 0167 } 0168 } 0169 if (ks) { 0170 QList<QCA::KeyStoreEntry> list = ks->entryList(); 0171 foreach (const QCA::KeyStoreEntry &e, list) { 0172 if (e.id() == kse.id() && kse.isAvailable()) { 0173 found = true; 0174 break; 0175 } 0176 } 0177 } 0178 } 0179 if (found) { 0180 // auto-accept 0181 handler.tokenOkay(id); 0182 return; 0183 } 0184 0185 QCA::KeyStoreEntry entry = event.keyStoreEntry(); 0186 QString name; 0187 if (!entry.isNull()) { 0188 name = Prompter::tr("Please make %1 (of %2) available").arg(entry.name(), entry.storeName()); 0189 } else { 0190 name = Prompter::tr("Please insert the '%1' token").arg(event.keyStoreInfo().name()); 0191 } 0192 0193 QString str = Prompter::tr("%1 and click OK.").arg(name); 0194 0195 QMessageBox msgBox(QMessageBox::Information, 0196 QApplication::instance()->applicationName() + ": " + tr("Prompt"), 0197 str, 0198 QMessageBox::Ok | QMessageBox::Cancel, 0199 0); 0200 token_prompt = &msgBox; 0201 auto_accept = false; 0202 if (msgBox.exec() == QMessageBox::Ok || auto_accept) 0203 handler.tokenOkay(id); 0204 else 0205 handler.reject(id); 0206 token_prompt = 0; 0207 } else 0208 handler.reject(id); 0209 0210 end: 0211 pending.removeFirst(); 0212 prompting = false; 0213 0214 if (!pending.isEmpty()) 0215 QMetaObject::invokeMethod(this, "nextEvent", Qt::QueuedConnection); 0216 } 0217 0218 void ks_available(const QString &keyStoreId) 0219 { 0220 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm); 0221 connect(ks, SIGNAL(updated()), SLOT(ks_updated())); 0222 connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable())); 0223 keyStores += ks; 0224 ks->startAsynchronousMode(); 0225 0226 // are we currently in a token-only prompt? 0227 if (token_prompt && pending.first().event.type() == QCA::Event::Token && 0228 pending.first().event.keyStoreEntry().isNull()) { 0229 // was the token we're looking for just inserted? 0230 if (pending.first().event.keyStoreInfo().id() == keyStoreId) { 0231 // auto-accept 0232 auto_accept = true; 0233 token_prompt->accept(); 0234 } 0235 } 0236 } 0237 0238 void ks_unavailable() 0239 { 0240 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0241 keyStores.removeAll(ks); 0242 delete ks; 0243 } 0244 0245 void ks_updated() 0246 { 0247 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0248 0249 // are we currently in a token-entry prompt? 0250 if (token_prompt && pending.first().event.type() == QCA::Event::Token && 0251 !pending.first().event.keyStoreEntry().isNull()) { 0252 QCA::KeyStoreEntry kse = pending.first().event.keyStoreEntry(); 0253 0254 // was the token of the entry we're looking for updated? 0255 if (pending.first().event.keyStoreInfo().id() == ks->id()) { 0256 // is the entry available? 0257 bool avail = false; 0258 QList<QCA::KeyStoreEntry> list = ks->entryList(); 0259 foreach (const QCA::KeyStoreEntry &e, list) { 0260 if (e.id() == kse.id()) { 0261 avail = kse.isAvailable(); 0262 break; 0263 } 0264 } 0265 if (avail) { 0266 // auto-accept 0267 auto_accept = true; 0268 token_prompt->accept(); 0269 } 0270 } 0271 } 0272 } 0273 }; 0274 0275 Prompter::Prompter(QObject *parent) 0276 : QObject(parent) 0277 { 0278 d = new Private(this); 0279 } 0280 0281 Prompter::~Prompter() 0282 { 0283 delete d; 0284 } 0285 0286 QCA::SecureArray Prompter::knownPassword(const QCA::Event &event) 0287 { 0288 Q_UNUSED(event); 0289 return QCA::SecureArray(); 0290 } 0291 0292 void Prompter::userSubmitted(const QCA::SecureArray &password, const QCA::Event &event) 0293 { 0294 Q_UNUSED(password); 0295 Q_UNUSED(event); 0296 } 0297 0298 #include "prompter.moc"