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