view ui/installwrapper.cpp @ 1371:23df332b2a4c

(issue179) Read install signature timestamp from config This also changes the way the sigDt is propgated to the MainWindow. It no longer uses the settings but hands it over as a parameter directly.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 24 Nov 2014 15:48:49 +0100
parents c8f698ca6355
children
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */
#include "installwrapper.h"

#include <QFileInfo>
#include <QProcess>
#include <QTemporaryFile>
#include <QApplication>
#include <QDir>
#include <QDebug>

#include "logging.h"
#include "util.h"
#include "binverify.h"

#define INSTALL_TIMEOUT 3600000 /* Wait up to an hour */

InstallWrapper::InstallWrapper(QObject* parent,
        const QString& path, const QStringList& choices):
    QThread(parent),
    mCertListFile(path),
    mChoices(choices)
{
}

QFileInfo getCinstProcInfo() {
    QFileInfo fi(QCoreApplication::applicationFilePath());
    QDir myDir = fi.absoluteDir();
    QString instProcName = "trustbridge-certificate-installer";
    if (!fi.suffix().isEmpty()) {
        instProcName += "." + fi.suffix();
    }
    return QFileInfo(myDir.absoluteFilePath(instProcName));
}

bool InstallWrapper::writeChoices(QTemporaryFile* choicesFile) const
{
    if (!choicesFile->open()) {
        return false;
    }

    foreach (const QString &b64data, mChoices) {
        syslog_info_printf ("Selected certificate: %s\n", b64data.toLatin1().constData());
        if (choicesFile->write(b64data.toLatin1()) == -1) {
            return false;
        }
        if (choicesFile->write("\n") == -1) {
            return false;
        }
    }

    choicesFile->close();

    return true;
}

void InstallWrapper::run()
{
    QTemporaryFile choicesFile;
    QFileInfo cinstProcInfo = getCinstProcInfo();

    QString cinstFileName = QDir::toNativeSeparators(
            getCinstProcInfo().absoluteFilePath());

    if (!cinstProcInfo.isExecutable()) {
        emit error(tr("Could not find certificate installation process."));
        return;
    }

    if (!writeChoices(&choicesFile)) {
        emit error(tr("Failed to write temporary file."));
        return;
    }

#ifdef WIN32
    bin_verify_result vres = verify_binary(cinstFileName.toUtf8().constData(),
            cinstFileName.toUtf8().size());

    if (vres.result != VerifyValid) {
        emit error(tr("Integrity check of the certificate installation process failed. ") 
                + "\n" + tr("Please reinstall the software."));
        return;
    }

    /* QProcess on Windows uses CreateProcess but we have to
     * use the runas shell command to get the UAC prompt if necessary.
     * So we have to handle the process ourself. Starting with
     * shell execute also means that we can not have stdout and stderr
     * redirection. This is the reason we use command line parameters
     * and not a pipe for communication. In debug mode the installer
     * also makes use of output debug string. */
    DWORD retval = 0;
    SHELLEXECUTEINFOA shExecInfo;

    memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOA));

    /* Windows needs each parameter packed in " */
    QString parameters = "\"list=" + mCertListFile +
        "\" \"choices=" + choicesFile.fileName() + "\"";

    if (g_debug) {
        parameters += " --debug";
    }

    shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOA);
    shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    if (!is_system_install()) {
        shExecInfo.lpVerb = "open";
    } else {
        shExecInfo.lpVerb = "runas";
    }
    shExecInfo.lpFile = strdup(cinstFileName.toLocal8Bit().constData());
    shExecInfo.lpParameters = strdup(parameters.toUtf8().constData());

    qDebug() << "Starting process " << cinstFileName <<" params: " << parameters;

    if (!ShellExecuteExA(&shExecInfo)) {
        char* errmsg = getLastErrorMsg();
        QString qerrmsg = QString::fromUtf8(errmsg);
        free(errmsg);
        emit error(tr("Error executing process: %1").arg(qerrmsg));
        fclose(vres.fptr);
        free((void*)shExecInfo.lpFile);
        free((void*)shExecInfo.lpParameters);
        return;
    }

    retval = WaitForSingleObject(shExecInfo.hProcess, INSTALL_TIMEOUT);

    free((void*)shExecInfo.lpFile);
    free((void*)shExecInfo.lpParameters);
    shExecInfo.lpFile = NULL;
    shExecInfo.lpParameters = NULL;

    if (retval != WAIT_OBJECT_0) {
        if (retval == WAIT_FAILED) {
            char* errmsg = getLastErrorMsg();
            QString qerrmsg = QString::fromUtf8(errmsg);
            free(errmsg);
            emit error (tr("Error monitoring process: %1").arg(qerrmsg));
            fclose(vres.fptr);
            return;
        } else {
            emit error (tr("Certificate installation timed out."));
            fclose(vres.fptr);
            return;
        }
    }

    if (GetExitCodeProcess(shExecInfo.hProcess, &retval)) {
        if (retval == STILL_ACTIVE) {
            qDebug() << "Process still running, huh..";
        }
    } else {
        char* errmsg = getLastErrorMsg();
        QString qerrmsg = QString::fromUtf8(errmsg);
        free(errmsg);
        emit error (tr("Failed to check process status: %1").arg(qerrmsg));
        CloseHandle(shExecInfo.hProcess);
        fclose(vres.fptr);
        return;
    }
    CloseHandle(shExecInfo.hProcess);
    fclose(vres.fptr);

    if (retval != 0) {
        /* TODO (issue135) make this nicer */
        emit error (tr("The process failed with return code. %1").arg(retval));
        return;
    }
#else /* WIN32 */
    QProcess installerProcess;
    QStringList parameters;

    choicesFile.setAutoRemove(false);
    parameters << "list=" + mCertListFile << "choices=" + choicesFile.fileName();

    if (g_debug) {
        parameters << "--debug";
    }

    bool sudo_started = false;
    bool use_sudo = is_admin() && is_system_install();
    if (use_sudo) {
        QStringList sudoPrograms;
        sudoPrograms << "pkexec" << "kdesudo" << "sudo";
        QStringList sudoParams;
        sudoParams << cinstProcInfo.absoluteFilePath() << parameters;
        installerProcess.setArguments(sudoParams);

        foreach (const QString &sProg, sudoPrograms) {
            installerProcess.setProgram(sProg);
            qDebug() << "Starting process " << sProg <<" params: " << sudoParams;
            installerProcess.start();
            if (!installerProcess.waitForStarted() ||
                installerProcess.state() == QProcess::NotRunning) {
                continue;
            } else {
                sudo_started = true;
                break;
            }
        }
    }

    /* Fallback to start without sudo */
    if (!use_sudo || !sudo_started) {
        installerProcess.setProgram(cinstProcInfo.absoluteFilePath());
        installerProcess.setArguments(parameters);

        qDebug() << "Starting process " << cinstFileName <<" params: " << parameters;
        installerProcess.start();
        if (!installerProcess.waitForStarted() ||
            installerProcess.state() == QProcess::NotRunning) {
            emit error (tr("Failed to start installer process."));
            return;
        }
    }

    installerProcess.waitForFinished();

    if (installerProcess.exitStatus() == QProcess::CrashExit) {
        /* Woops */
        emit error (tr("Failed to complete installation."));
        return;
    } else if (installerProcess.exitStatus() != QProcess::NormalExit) {
        /* Can not Happen. there are only those two values but maybe
         * qt changed.. */
        emit error (tr("Failed to complete installation."));
        return;
    }

    if (installerProcess.exitCode() == 0) {
        qDebug() << "output: " << installerProcess.readAllStandardOutput();
    } else {
        /* TODO (issue135) handle errors defined by errorcodes.h */
        qDebug() << "Installer Process returned: " << installerProcess.exitCode();
        qDebug() << "output: " << installerProcess.readAllStandardOutput();
        return;
    }
#endif
    emit installationSuccessful();
}

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