security.cpp
00001 /*************************************************************************** 00002 security.cpp - description 00003 ------------------- 00004 begin : Thu Jun 24 11:22:12 2004 00005 copyright : (C) 2004, 2005 by Andras Mantia <amantia@kde.org> 00006 ***************************************************************************/ 00007 00008 /*************************************************************************** 00009 * * 00010 * This program is free software; you can redistribute it and/or modify * 00011 * it under the terms of the GNU Library General Public License as * 00012 * published by the Free Software Foundation; version 2 of the License. * 00013 * * 00014 ***************************************************************************/ 00015 00016 //qt includes 00017 #include <qfile.h> 00018 #include <qfileinfo.h> 00019 #include <qstringlist.h> 00020 #include <qtimer.h> 00021 00022 //kde includes 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 //app includes 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 //check the MD5 sum 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 //verify the signature 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 //create the MD5 sum 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 //verify the signature 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"