diff ui/createinstallerdialog.cpp @ 571:6c4fff146999

Implement codesigning in the administrator tool
author Andre Heinecke <aheinecke@intevation.de>
date Fri, 23 May 2014 16:17:18 +0000
parents 421b69eeffe3
children 49168bcb02e2
line wrap: on
line diff
--- a/ui/createinstallerdialog.cpp	Fri May 23 16:16:33 2014 +0000
+++ b/ui/createinstallerdialog.cpp	Fri May 23 16:17:18 2014 +0000
@@ -19,10 +19,25 @@
 #include <QStyle>
 #include <QApplication>
 #include <QMessageBox>
+#include <QTemporaryDir>
+
+/* Static information used in codesigning */
+#ifndef SIGN_HASH
+#define SIGN_HASH "sha256"
+#endif
+#ifndef SIGN_URL
+#define SIGN_URL "https://wald.intevation.org/projects/trustbridge/"
+#endif
+#ifndef SIGN_PUBLISHER
+#define SIGN_PUBLISHER "TrustBridge Test with ümlaut"
+#endif
+
 
 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) :
     QDialog(parent),
-    mProgress(this)
+    mProgress(this),
+    mInstallerPath(),
+    mCurrentWorkingDir(NULL)
 {
     QSettings settings;
     setWindowTitle(tr("Create binary installer"));
@@ -116,7 +131,6 @@
     setLayout(topLayout);
 
     mProgress.setWindowModality(Qt::WindowModal);
-    mProgress.setLabelText(tr("Creating installer package..."));
     mProgress.setCancelButton(0);
     mProgress.setRange(0,0);
     mProgress.setMinimumDuration(0);
@@ -162,9 +176,18 @@
 
 void CreateInstallerDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
 {
+    if (mCurrentWorkingDir) {
+        delete mCurrentWorkingDir;
+        mCurrentWorkingDir = NULL;
+    }
     FinishedDialog *fin = new FinishedDialog(0, tr("Created installer in %1.")
             .arg(mSaveFile->text()), mNSISProc.readAll(), false);
     qDebug() << "Finished: " << mNSISProc.readAll();
+    mProgress.setLabelText(tr("Signing installer package..."));
+    if (!signFile(mInstallerPath)) {
+        showErrorMessage(tr("Failed to sign installer package."));
+        QFile::remove(mInstallerPath);
+    }
     mProgress.cancel();
     fin->show();
     close();
@@ -172,12 +195,17 @@
 
 void CreateInstallerDialog::processError(QProcess::ProcessError error)
 {
+    if (mCurrentWorkingDir) {
+        delete mCurrentWorkingDir;
+        mCurrentWorkingDir = NULL;
+    }
     qDebug() << "Error: " << mNSISProc.readAll();
     mProgress.cancel();
 }
 
 void CreateInstallerDialog::createInstaller()
 {
+    mProgress.setLabelText(tr("Creating installer package..."));
     QDir binDir(mBinaryFolder->text());
     QDir outDir(mSaveFile->text());
     if (mBinaryFolder->text().isEmpty() || !binDir.exists()) {
@@ -201,28 +229,39 @@
         return;
     }
 
+    QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows");
+
+    if (!signedFilesDir) {
+        /* Error messages should have been shown by the codesign function */
+        return;
+    }
+
+    mProgress.setLabelText(tr("Creating NSIS package..."));
+
     /* Copy windows directory contents to tmpdir */
     QStringList arguments;
     mNSISProc.setProgram("makensis");
     mNSISProc.setProcessChannelMode(QProcess::MergedChannels);
     mNSISProc.setWorkingDirectory(outDir.path());
 #ifdef Q_OS_WIN
-    arguments << QString::fromLatin1("/Dfiles_dir=") + binDir.path().replace("/", "\\") + "\\windows";
+    arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\");
     arguments << "/Dpath_sep=\\";
     foreach (const QString &key, keys) {
         QString value = options.value(key, QString()).toString();
         if (key == "setupname") {
             value = value.arg(outDir.path().replace("/", "\\") + "\\");
+            mInstallerPath = value;
         }
         arguments << QString::fromLatin1("/D%1=%2").arg(key, value);
     }
 #else
-    arguments << QString::fromLatin1("-Dfiles_dir=") + binDir.path() + "/windows";
+    arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path();
     arguments << "-Dpath_sep=/";
     foreach (const QString &key, keys) {
         QString value = options.value(key, QString()).toString();
         if (key == "setupname") {
             value = value.arg(outDir.path() + "/");
+            mInstallerPath = value;
         }
         arguments << QString::fromLatin1("-D%1=%2").arg(key, value);
     }
@@ -242,6 +281,85 @@
     }
 }
 
+bool CreateInstallerDialog::signFile(QString filePath) {
+    QProcess signProc;
+    signProc.setProcessChannelMode(QProcess::MergedChannels);
+    signProc.setProgram("osslsigncode");
+    QStringList arguments;
+
+    QSettings mySettings;
+
+    QString publisher = mySettings.value("sign_publisher", SIGN_PUBLISHER).toString();
+    QString url = mySettings.value("sign_url", SIGN_URL).toString();
+    QString hash = mySettings.value("sign_hash", SIGN_HASH).toString();
+
+    arguments << "sign" << "-certs" << mCertFile->text() << "-key"
+              << mCertFile->text() << "-n" << publisher << "-i" <<
+              url << "-h" << hash << "-in" << filePath << "-out" << filePath + ".signed";
+
+    qDebug() << "Starting osslsigncode with arguments: " << arguments;
+    signProc.setArguments(arguments);
+    signProc.start();
+
+    if (!signProc.waitForFinished(30000)) {
+        qDebug() << "Signing takes longer then 30 seconds. Aborting.";
+        return false;
+    }
+
+    if (signProc.exitStatus() != QProcess::NormalExit ||
+        signProc.exitCode() != 0) {
+        qDebug() << "Error process returned: " << signProc.exitCode();
+        qDebug() << "Output: " << signProc.readAllStandardOutput();
+        return false;
+    }
+
+    if (!QFile::remove(filePath)) {
+        qDebug() << "Failed to remove file.";
+        return false;
+    }
+    if (!QFile::copy(filePath + ".signed", filePath)) {
+        qDebug() << "Failed to copy signed file in place.";
+        return false;
+    }
+    if (!QFile::remove(filePath + ".signed")) {
+        qDebug() << "Failed to remove signed.";
+        return false;
+    }
+    return true;
+}
+
+
+QTemporaryDir *CreateInstallerDialog::codesignBinaries(const QDir& sourceDir) {
+    QTemporaryDir* target = new QTemporaryDir();
+    /* Copy all files from the source dir to a temporary location */
+    mProgress.setLabelText(tr("Signing binaries..."));
+
+    mProgress.show();
+    foreach (const QFileInfo& entry, sourceDir.entryInfoList()) {
+        QString targetPath = target->path() + QString::fromLatin1("/") + entry.fileName();
+        if (entry.fileName() == "." || entry.fileName() == "..") {
+            continue;
+        }
+        if (!QFile::copy(entry.absoluteFilePath(), targetPath)) {
+            qDebug() << "Failed to copy: " << entry.absoluteFilePath() << " to: " << targetPath;
+            showErrorMessage(tr("Failed to copy binaries to temporary location."));
+            mProgress.cancel();
+            return NULL;
+        }
+        if (entry.suffix() == "exe") {
+            if (!signFile(targetPath)) {
+                showErrorMessage(tr("Failed to sign binaries with osslsigncode.\n"
+                            "Please check that %1 is a valid code signing certificate and that"
+                            "osslsigncode can be found in the PATH.").arg(mCertFile->text()));
+                mProgress.cancel();
+                return NULL;
+            }
+        }
+    }
+    mProgress.cancel();
+    return target;
+}
+
 FinishedDialog::FinishedDialog(QDialog *parent,
         QString msg, QString details, bool isErr):
     QDialog(parent)

http://wald.intevation.org/projects/trustbridge/