aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
aheinecke@404:  * Software engineering by Intevation GmbH
aheinecke@404:  *
aheinecke@404:  * This file is Free Software under the GNU GPL (v>=2)
aheinecke@404:  * and comes with ABSOLUTELY NO WARRANTY!
aheinecke@404:  * See LICENSE.txt for details.
aheinecke@404:  */
aheinecke@100: #include "cinstprocesstest.h"
aheinecke@100: #include "certificatelist.h"
aheinecke@100: #include "errorcodes.h"
aheinecke@298: #include "common.h"
aheinecke@100: 
aheinecke@100: #include <QDebug>
aheinecke@100: #include <QDir>
aheinecke@100: #include <QFile>
aheinecke@100: #include <QProcess>
aheinecke@100: 
aheinecke@313: #include <stdlib.h>
aheinecke@313: 
andre@1176: #define CINST_PATH_CANDIDATES "../../cinst/trustbridge-certificate-installer" << \
andre@1176:     "trustbridge-certificate-installer" << \
andre@1176:     "../../cinst/trustbridge-certificate-installer.exe" \
andre@1176:     << "trustbridge-certificate-installer.exe";
aheinecke@100: 
aheinecke@294: QProcess *CinstProcessTest::startCinstProcess(const QStringList& args) {
aheinecke@294:     QStringList cinstCandidates;
aheinecke@294:     cinstCandidates << CINST_PATH_CANDIDATES;
aheinecke@294:     QString processPath;
aheinecke@294:     foreach (const QString& candidate, cinstCandidates) {
aheinecke@294:         QFileInfo fi(candidate);
aheinecke@294:         if (fi.isExecutable()) {
aheinecke@294:             processPath = candidate;
aheinecke@294:             break;
aheinecke@294:         }
aheinecke@294:     }
aheinecke@294: 
andre@1060:     QStringList newArgs = args;
andre@1060:     newArgs << "--debug";
andre@1060: 
aheinecke@100:     QProcess *installerProcess = new QProcess();
andre@1060:     installerProcess->setArguments(newArgs);
aheinecke@294:     installerProcess->setProgram(processPath);
aheinecke@100:     installerProcess->start();
aheinecke@100:     installerProcess->waitForStarted();
aheinecke@100:     return installerProcess;
aheinecke@100: }
aheinecke@100: 
aheinecke@151: #define VERIFY_PROC_DEBUG(x) \
aheinecke@151:     if (! x ) { \
aheinecke@151:         qDebug() << "Stdout:" << proc->readAllStandardOutput(); \
aheinecke@151:         qDebug() << "Stderr:" << proc->readAllStandardError(); \
aheinecke@151:         qDebug() << "Exit code: " << proc->exitCode(); \
andre@305:         qDebug() << "Exit status: " << proc->exitStatus(); \
aheinecke@151:     } \
aheinecke@151:     QVERIFY(x)
aheinecke@151: 
aheinecke@100: void finishVerify(QProcess *proc, int exitCode) {
aheinecke@100:     proc->closeWriteChannel();
aheinecke@100:     proc->waitForFinished();
aheinecke@151:     VERIFY_PROC_DEBUG(proc->exitStatus() == QProcess::NormalExit);
aheinecke@151:     VERIFY_PROC_DEBUG(proc->exitCode() == exitCode);
aheinecke@100:     delete proc;
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testValidInput() {
aheinecke@295:     QStringList args;
aheinecke@295:     args << "list=" + validListFile.fileName();
aheinecke@100: 
aheinecke@295:     QTemporaryFile instructions;
aheinecke@295:     instructions.open();
aheinecke@295:     foreach (const Certificate &cert, validList.getCertificates()) {
aheinecke@295:         instructions.write(cert.base64Line().toLatin1());
aheinecke@295:         instructions.write("\n");
aheinecke@295:     }
aheinecke@295:     instructions.close();
aheinecke@100: 
aheinecke@439:     args << "choices=" + instructions.fileName();
aheinecke@100: 
aheinecke@295:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@159:     finishVerify(installerProcess, ERR_NO_ERROR);
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::initTestCase() {
aheinecke@295:     QFile valid(":/list-valid-signed.txt");
aheinecke@295:     valid.open(QIODevice::ReadOnly);
aheinecke@295:     validListFile.open();
aheinecke@295:     validListFile.write(valid.readAll());
aheinecke@295:     valid.close();
aheinecke@295:     validListFile.close();
aheinecke@295:     validList = CertificateList(validListFile.fileName().toLocal8Bit().data());
aheinecke@295: 
aheinecke@295:     QVERIFY(validList.isValid());
aheinecke@295: 
aheinecke@295:     QFile invalid(":/list-invalid-signed.txt");
aheinecke@295:     invalid.open(QIODevice::ReadOnly);
aheinecke@295:     invalidListFile.open();
aheinecke@295:     invalidListFile.write(invalid.readAll());
aheinecke@295:     invalid.close();
aheinecke@295:     invalidListFile.close();
aheinecke@295:     invalidList = CertificateList(invalidListFile.fileName().toLocal8Bit().data());
aheinecke@295: 
aheinecke@295:     QVERIFY(!invalidList.isValid());
aheinecke@295: 
aheinecke@295:     QFile other(":/list-valid-other-signature.txt");
aheinecke@295:     other.open(QIODevice::ReadOnly);
aheinecke@295:     otherListFile.open();
aheinecke@295:     otherListFile.write(other.readAll());
aheinecke@295:     other.close();
aheinecke@295:     otherListFile.close();
aheinecke@295:     otherList = CertificateList(otherListFile.fileName().toLocal8Bit().data());
aheinecke@295: 
aheinecke@295:     QVERIFY(!otherList.isValid());
andre@306: 
andre@904: /* Set HOME or APPDATA so that nss stores are not touched
andre@306:  * see nsstest for the real test of that code */
andre@306: #ifdef WIN32
aheinecke@313:     {
aheinecke@314:         char buf[fakeHome.path().toLocal8Bit().size() + 9];
aheinecke@314:         snprintf(buf, fakeHome.path().toLocal8Bit().size() + 9,
aheinecke@314:                 "APPDATA=%s",fakeHome.path().toLocal8Bit().constData());
aheinecke@313:         QVERIFY(_putenv (buf) != -1);
aheinecke@313:     }
andre@306: #else
andre@306:     QVERIFY(!setenv ("HOME", fakeHome.path().toLocal8Bit().constData(), 1));
andre@306: #endif
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testNoList() {
aheinecke@100:     /* No list */
aheinecke@296:     QTemporaryFile emptyFile;
aheinecke@296:     emptyFile.open();
aheinecke@296:     emptyFile.close();
aheinecke@100: 
aheinecke@296:     QStringList args;
aheinecke@296:     args << "list=" + emptyFile.fileName();
aheinecke@296: 
aheinecke@296:     QTemporaryFile instructions;
aheinecke@296:     instructions.open();
aheinecke@249:     foreach (const Certificate &cert, validList.getCertificates()) {
aheinecke@296:         instructions.write(cert.base64Line().toLatin1());
aheinecke@296:         instructions.write("\n");
aheinecke@100:     }
aheinecke@296:     instructions.close();
aheinecke@296: 
aheinecke@439:     args << "choices=" + instructions.fileName();
aheinecke@296: 
aheinecke@296:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@100:     finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST);
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testGarbageInput() {
aheinecke@298:     QStringList args;
aheinecke@100: 
aheinecke@298:     QString garbage = getRandomDataFile(21*1024*1024);
aheinecke@298:     args << "list=" + garbage;
aheinecke@298: 
aheinecke@298:     QTemporaryFile instructions;
aheinecke@298:     instructions.open();
aheinecke@298:     foreach (const Certificate &cert, validList.getCertificates()) {
aheinecke@298:         instructions.write(cert.base64Line().toLatin1());
aheinecke@298:         instructions.write("\n");
aheinecke@298:     }
aheinecke@298:     instructions.close();
aheinecke@298: 
aheinecke@439:     args << "choices=" + instructions.fileName();
aheinecke@298: 
aheinecke@298:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@298:     /* If the following failed there may be leftovers in /tmp */
aheinecke@298:     finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST);
aheinecke@298:     QVERIFY(QFile::remove(garbage));
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testNoInput() {
aheinecke@298:     QStringList args;
aheinecke@298:     args << "list=foobazbuf";
aheinecke@439:     args << "choices=bazbuffoo";
aheinecke@298:     QProcess* installerProcess;
aheinecke@298:     installerProcess = startCinstProcess(args);
aheinecke@100:     finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST);
aheinecke@100: }
aheinecke@100: 
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testNoInstructions() {
aheinecke@100:     /* No instructions */
aheinecke@298:     QTemporaryFile emptyFile;
aheinecke@298:     emptyFile.open();
aheinecke@298:     emptyFile.close();
aheinecke@100: 
aheinecke@298:     QStringList args;
aheinecke@298:     args << "list=" + validListFile.fileName();
aheinecke@439:     args << "choices=" + emptyFile.fileName();
aheinecke@298: 
aheinecke@298:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@100:     finishVerify(installerProcess, ERR_NO_INSTRUCTIONS);
aheinecke@100: }
aheinecke@100: 
aheinecke@100: void CinstProcessTest::testInvalidInstruction() {
aheinecke@298:     QStringList args;
aheinecke@298:     args << "list=" + validListFile.fileName();
aheinecke@100: 
aheinecke@298:     QTemporaryFile instructions;
aheinecke@298:     instructions.open();
aheinecke@298:     foreach (const Certificate &cert, validList.getCertificates()) {
aheinecke@298:         instructions.write(cert.base64Line().toLatin1());
aheinecke@298:         instructions.write("\n");
aheinecke@298:     }
aheinecke@298:     instructions.write("I:ABCDEF\n");
aheinecke@298:     instructions.close();
aheinecke@100: 
aheinecke@439:     args << "choices=" + instructions.fileName();
aheinecke@298: 
aheinecke@298:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@100: 
aheinecke@100:     finishVerify(installerProcess, ERR_INVALID_INSTRUCTIONS);
aheinecke@100: }
aheinecke@100: 
aheinecke@151: void CinstProcessTest::testUninstall() {
aheinecke@298:     QStringList args;
aheinecke@298:     args << "list=" + validListFile.fileName();
aheinecke@439:     args << "choices=uninstall";
aheinecke@151: 
aheinecke@298:     QProcess* installerProcess = startCinstProcess(args);
aheinecke@151: 
aheinecke@159:     finishVerify(installerProcess, ERR_NO_ERROR);
aheinecke@151: }
aheinecke@151: 
andre@1163: bool g_debug = true;
andre@1163: 
aheinecke@100: QTEST_GUILESS_MAIN (CinstProcessTest);