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