00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "security.h"
00024
00025
00026 #include <QtCore/QFile>
00027 #include <QtCore/QFileInfo>
00028 #include <QtCore/QStringList>
00029 #include <QtCore/QTextIStream>
00030 #include <QtCore/QTimer>
00031
00032
00033 #include <kdebug.h>
00034 #include <kinputdialog.h>
00035 #include <klocale.h>
00036 #include <kcodecs.h>
00037 #include <kmessagebox.h>
00038 #include <kpassworddialog.h>
00039 #include <kprocess.h>
00040
00041 using namespace KNS;
00042
00043 Security::Security()
00044 {
00045 m_keysRead = false;
00046 m_gpgRunning = false;
00047 readKeys();
00048 readSecretKeys();
00049 }
00050
00051
00052 Security::~Security()
00053 {
00054 }
00055
00056 void Security::readKeys()
00057 {
00058 if (m_gpgRunning) {
00059 QTimer::singleShot(5, this, SLOT(readKeys()));
00060 return;
00061 }
00062 m_runMode = List;
00063 m_keys.clear();
00064 m_process = new KProcess();
00065 *m_process << "gpg"
00066 << "--no-secmem-warning"
00067 << "--no-tty"
00068 << "--with-colon"
00069 << "--list-keys";
00070 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
00071 this, SLOT(slotFinished(int, QProcess::ExitStatus)));
00072 connect(m_process, SIGNAL(readyReadStandardOutput()),
00073 this, SLOT(slotReadyReadStandardOutput()));
00074 m_process->start();
00075 if (!m_process->waitForStarted()) {
00076 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and retrieve the available keys. Make sure that <i>gpg</i> is installed, otherwise verification of downloaded resources will not be possible.</qt>"));
00077 delete m_process;
00078 m_process = 0;
00079 } else
00080 m_gpgRunning = true;
00081 }
00082
00083 void Security::readSecretKeys()
00084 {
00085 if (m_gpgRunning) {
00086 QTimer::singleShot(5, this, SLOT(readSecretKeys()));
00087 return;
00088 }
00089 m_runMode = ListSecret;
00090 m_process = new KProcess();
00091 *m_process << "gpg"
00092 << "--no-secmem-warning"
00093 << "--no-tty"
00094 << "--with-colon"
00095 << "--list-secret-keys";
00096 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
00097 this, SLOT(slotFinished(int, QProcess::ExitStatus)));
00098 connect(m_process, SIGNAL(readyReadStandardOutput()),
00099 this, SLOT(slotReadyReadStandardOutput()));
00100 m_process->start();
00101 if (!m_process->waitForStarted()) {
00102 delete m_process;
00103 m_process = 0;
00104 } else
00105 m_gpgRunning = true;
00106 }
00107
00108 void Security::slotFinished(int exitCode, QProcess::ExitStatus exitStatus)
00109 {
00110 if (exitStatus != QProcess::NormalExit) {
00111 m_gpgRunning = false;
00112 delete m_process;
00113 m_process = 0;
00114 return;
00115 }
00116 switch (m_runMode) {
00117 case ListSecret:
00118 m_keysRead = true;
00119 break;
00120 case Verify: emit validityResult(m_result);
00121 break;
00122 case Sign: emit fileSigned(m_result);
00123 break;
00124
00125 }
00126 m_gpgRunning = false;
00127 delete m_process;
00128 m_process = 0;
00129
00130 Q_UNUSED(exitCode);
00131 }
00132
00133 void Security::slotReadyReadStandardOutput()
00134 {
00135 QString data;
00136 while (m_process->canReadLine()) {
00137 data = QString::fromLocal8Bit(m_process->readLine());
00138 switch (m_runMode) {
00139 case List:
00140 case ListSecret:
00141 if (data.startsWith("pub") || data.startsWith("sec")) {
00142 KeyStruct key;
00143 if (data.startsWith("pub"))
00144 key.secret = false;
00145 else
00146 key.secret = true;
00147 QStringList line = data.split(":", QString::KeepEmptyParts);
00148 key.id = line[4];
00149 QString shortId = key.id.right(8);
00150 QString trustStr = line[1];
00151 key.trusted = false;
00152 if (trustStr == "u" || trustStr == "f")
00153 key.trusted = true;
00154 data = line[9];
00155 key.mail = data.section('<', -1, -1);
00156 key.mail.truncate(key.mail.length() - 1);
00157 key.name = data.section('<', 0, 0);
00158 if (key.name.contains("("))
00159 key.name = key.name.section('(', 0, 0);
00160 m_keys[shortId] = key;
00161 }
00162 break;
00163 case Verify:
00164 data = data.section("]", 1, -1).trimmed();
00165 if (data.startsWith("GOODSIG")) {
00166 m_result &= SIGNED_BAD_CLEAR;
00167 m_result |= SIGNED_OK;
00168 QString id = data.section(" ", 1 , 1).right(8);
00169 if (!m_keys.contains(id)) {
00170 m_result |= UNKNOWN;
00171 } else {
00172 m_signatureKey = m_keys[id];
00173 }
00174 } else
00175 if (data.startsWith("NO_PUBKEY")) {
00176 m_result &= SIGNED_BAD_CLEAR;
00177 m_result |= UNKNOWN;
00178 } else
00179 if (data.startsWith("BADSIG")) {
00180 m_result |= SIGNED_BAD;
00181 QString id = data.section(" ", 1 , 1).right(8);
00182 if (!m_keys.contains(id)) {
00183 m_result |= UNKNOWN;
00184 } else {
00185 m_signatureKey = m_keys[id];
00186 }
00187 } else
00188 if (data.startsWith("TRUST_ULTIMATE")) {
00189 m_result &= SIGNED_BAD_CLEAR;
00190 m_result |= TRUSTED;
00191 }
00192 break;
00193
00194 case Sign:
00195 if (data.contains("passphrase.enter")) {
00196 KeyStruct key = m_keys[m_secretKey];
00197 KPasswordDialog dlg;
00198 dlg.setPrompt(i18n("<qt>Enter passphrase for key <b>0x%1</b>, belonging to<br /><i>%2<%3></i><br />:</qt>", m_secretKey, key.name, key.mail));
00199 if (dlg.exec()) {
00200 m_process->write(dlg.password().toLocal8Bit() + '\n');
00201 } else {
00202 m_result |= BAD_PASSPHRASE;
00203 m_process->kill();
00204 return;
00205 }
00206 } else
00207 if (data.contains("BAD_PASSPHRASE")) {
00208 m_result |= BAD_PASSPHRASE;
00209 }
00210 break;
00211 }
00212 }
00213 }
00214
00215 void Security::checkValidity(const QString& filename)
00216 {
00217 m_fileName = filename;
00218 slotCheckValidity();
00219 }
00220
00221 void Security::slotCheckValidity()
00222 {
00223 if (!m_keysRead || m_gpgRunning) {
00224 QTimer::singleShot(5, this, SLOT(slotCheckValidity()));
00225 return;
00226 }
00227 if (m_keys.count() == 0) {
00228 emit validityResult(-1);
00229 return;
00230 }
00231
00232 m_result = 0;
00233 m_runMode = Verify;
00234 QFileInfo f(m_fileName);
00235
00236 QString md5sum;
00237 const char* c = "";
00238 KMD5 context(c);
00239 QFile file(m_fileName);
00240 if (file.open(QIODevice::ReadOnly)) {
00241 context.reset();
00242 context.update(file);
00243 md5sum = context.hexDigest();
00244 file.close();
00245 }
00246 file.setFileName(f.path() + "/md5sum");
00247 if (file.open(QIODevice::ReadOnly)) {
00248 QByteArray md5sum_file;
00249 file.readLine(md5sum_file.data(), 50);
00250 if (!md5sum_file.isEmpty() && QString(md5sum_file).startsWith(md5sum))
00251 m_result |= MD5_OK;
00252 file.close();
00253 }
00254 m_result |= SIGNED_BAD;
00255 m_signatureKey.id = "";
00256 m_signatureKey.name = "";
00257 m_signatureKey.mail = "";
00258 m_signatureKey.trusted = false;
00259
00260
00261 m_process = new KProcess();
00262 *m_process << "gpg"
00263 << "--no-secmem-warning"
00264 << "--status-fd=2"
00265 << "--command-fd=0"
00266 << "--verify"
00267 << f.path() + "/signature"
00268 << m_fileName;
00269 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
00270 this, SLOT(slotFinished(int, QProcess::ExitStatus)));
00271 connect(m_process, SIGNAL(readyReadStandardOutput()),
00272 this, SLOT(slotReadyReadStandardOutput()));
00273 m_process->start();
00274 if (m_process->waitForStarted())
00275 m_gpgRunning = true;
00276 else {
00277 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and check the validity of the file. Make sure that <i>gpg</i> is installed, otherwise verification of downloaded resources will not be possible.</qt>"));
00278 emit validityResult(0);
00279 delete m_process;
00280 m_process = 0;
00281 }
00282 }
00283
00284 void Security::signFile(const QString &fileName)
00285 {
00286 m_fileName = fileName;
00287 slotSignFile();
00288 }
00289
00290 void Security::slotSignFile()
00291 {
00292 if (!m_keysRead || m_gpgRunning) {
00293 QTimer::singleShot(5, this, SLOT(slotSignFile()));
00294 return;
00295 }
00296
00297 QStringList secretKeys;
00298 for (QMap<QString, KeyStruct>::Iterator it = m_keys.begin(); it != m_keys.end(); ++it) {
00299 if (it.value().secret)
00300 secretKeys.append(it.key());
00301 }
00302
00303 if (secretKeys.count() == 0) {
00304 emit fileSigned(-1);
00305 return;
00306 }
00307
00308 m_result = 0;
00309 QFileInfo f(m_fileName);
00310
00311
00312 QString md5sum;
00313 const char* c = "";
00314 KMD5 context(c);
00315 QFile file(m_fileName);
00316 if (file.open(QIODevice::ReadOnly)) {
00317 context.reset();
00318 context.update(file);
00319 md5sum = context.hexDigest();
00320 file.close();
00321 }
00322 file.setFileName(f.path() + "/md5sum");
00323 if (file.open(QIODevice::WriteOnly)) {
00324 QTextStream stream(&file);
00325 stream << md5sum;
00326 m_result |= MD5_OK;
00327 file.close();
00328 }
00329
00330 if (secretKeys.count() > 1) {
00331 bool ok;
00332 secretKeys = KInputDialog::getItemList(i18n("Select Signing Key"), i18n("Key used for signing:"), secretKeys, QStringList(secretKeys[0]), false, &ok);
00333 if (ok)
00334 m_secretKey = secretKeys[0];
00335 else {
00336 emit fileSigned(0);
00337 return;
00338 }
00339 } else
00340 m_secretKey = secretKeys[0];
00341
00342
00343 m_process = new KProcess();
00344 *m_process << "gpg"
00345 << "--no-secmem-warning"
00346 << "--status-fd=2"
00347 << "--command-fd=0"
00348 << "--no-tty"
00349 << "--detach-sign"
00350 << "-u"
00351 << m_secretKey
00352 << "-o"
00353 << f.path() + "/signature"
00354 << m_fileName;
00355 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
00356 this, SLOT(slotFinished(int, QProcess::ExitStatus)));
00357 connect(m_process, SIGNAL(readyReadStandardOutput()),
00358 this, SLOT(slotReadyReadStandardOutput()));
00359 m_runMode = Sign;
00360 m_process->start();
00361 if (m_process->waitForStarted())
00362 m_gpgRunning = true;
00363 else {
00364 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and sign the file. Make sure that <i>gpg</i> is installed, otherwise signing of the resources will not be possible.</qt>"));
00365 emit fileSigned(0);
00366 delete m_process;
00367 m_process = 0;
00368 }
00369 }
00370
00371 #include "security.moc"