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:  */
andre@307: #include <cert.h>
andre@307: #include <certdb.h>
andre@307: #include <certt.h>
andre@307: 
andre@307: #include <nss.h>
andre@307: #include <pk11pub.h>
andre@307: 
andre@304: #include "nsstest.h"
andre@304: #include "nssstore.h"
andre@304: #include "strhelp.h"
andre@304: 
andre@304: #include <QTest>
aheinecke@330: #include <QTextCodec>
aheinecke@330: 
aheinecke@330: #ifdef WIN32
aheinecke@330: #define endl "\r\n"
aheinecke@330: #endif
andre@304: 
andre@307: QList<QByteArray> NSSTest::get_nss_certs (QTemporaryDir *nssDir)
andre@307: {
andre@307:   CERTCertList *list;
andre@307:   CERTCertListNode *node;
andre@307:   QList<QByteArray> retval;
andre@304: 
andre@307:   if (NSS_Initialize(nssDir->path().toLocal8Bit().constData(),
andre@307:               "", "", "secmod.db", NSS_INIT_READONLY)
andre@307:       == SECSuccess)
andre@307:     {
andre@307:       list = PK11_ListCerts(PK11CertListAll, NULL);
andre@307:       for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
andre@307:            node = CERT_LIST_NEXT(node)) {
andre@307:         retval << QByteArray((const char*)node->cert->derCert.data,
andre@307:                 (int)node->cert->derCert.len);
andre@307:       }
andre@307:       CERT_DestroyCertList(list);
andre@307:       NSS_Shutdown();
andre@307:     }
andre@307:   else
andre@307:     {
andre@307:       qDebug("Could not open nss certificate store!\n");
andre@307:     }
andre@307:   return retval;
andre@307: }
andre@307: 
andre@307: void NSSTest::setupTestDir(QTemporaryDir *nssDir)
andre@307: {
andre@307:     /* Copy the empty nss db in the temporary dir */
aheinecke@330:     QVERIFY(QFile::copy(":/nss/cert8.db", nssDir->path() + "/" +"cert8.db"));
aheinecke@330:     QVERIFY(QFile::copy(":/nss/key3.db", nssDir->path() + "/" +"key3.db"));
aheinecke@330:     QVERIFY(QFile::copy(":/nss/secmod.db", nssDir->path() + "/" +"secmod.db"));
andre@307: 
andre@307:     QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"cert8.db",
andre@304:                 QFileDevice::ReadOwner | QFileDevice::WriteOwner));
andre@307:     QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"key3.db",
andre@304:                 QFileDevice::ReadOwner | QFileDevice::WriteOwner));
andre@307:     QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"secmod.db",
andre@304:                 QFileDevice::ReadOwner | QFileDevice::WriteOwner));
andre@346: //    nssDir->setAutoRemove(false);
andre@307: }
andre@307: 
andre@307: void NSSTest::initTestCase() {
aheinecke@330:     QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); /* use system default */
andre@304: 
andre@304:     /* Set up a temporary list */
andre@371: #ifdef RELEASE_BUILD
andre@371:     QFile res(":/list-valid-signed-release.txt");
andre@371: #else
andre@304:     QFile res(":/list-valid-signed.txt");
andre@371: #endif
andre@304:     res.open(QIODevice::ReadOnly);
andre@304:     validListFile.open();
andre@304:     validListFile.write(res.readAll());
andre@304:     validListFile.close();
andre@304: 
andre@307:     setupTestDir(&ffNSSDir);
andre@307:     setupTestDir(&tbNSSDir);
andre@304: 
andre@304:     validList = CertificateList(validListFile.fileName().toLocal8Bit().data());
andre@304: 
andre@371:     QVERIFY(validList.isValid());
andre@371: 
andre@304:     /* Create the profiles.ini `s set environment variables*/
andre@346: //    fakeHome.setAutoRemove(false);
andre@304: #ifndef WIN32
andre@304:     fakeFirefoxDir = QDir(fakeHome.path() + "/.mozilla/firefox");
andre@304:     fakeThunderbirdDir = QDir(fakeHome.path() + "/.thunderbird");
aheinecke@492: 
aheinecke@492:     /* Copy the current systems NSSSHARED db in the fake home for benchmark tests */
aheinecke@492:     QDir fakeNSSSharedDir = fakeHome.path() + "/.pki/nssdb";
aheinecke@492:     QVERIFY(fakeNSSSharedDir.mkpath(fakeNSSSharedDir.path()));
andre@830:     QVERIFY(QFile::copy(":/nss/pki-nssdb/cert9.db",
aheinecke@492:             fakeNSSSharedDir.path() + "cert9.db"));
andre@830:     QVERIFY(QFile::copy(":/nss/pki-nssdb/key4.db",
aheinecke@492:             fakeNSSSharedDir.path() + "key4.db"));
andre@830:     QFile pkcs11(":/nss/pki-nssdb/pkcs11.txt");
andre@830:     pkcs11.open(QIODevice::ReadOnly);
andre@830: 
andre@830:     QString pkcs11content = pkcs11.readAll();
andre@830:     pkcs11.close();
andre@830:     pkcs11content.replace("TOREPLACE", fakeNSSSharedDir.path());
andre@830:     QFile p11outfile (fakeNSSSharedDir.path() + "pkcs11.txt");
andre@830:     p11outfile.open(QIODevice::WriteOnly);
andre@830:     p11outfile.write(pkcs11content.toUtf8());
andre@830:     p11outfile.close();
aheinecke@492: 
aheinecke@492:     QVERIFY(!setenv ("HOME", fakeHome.path().toLocal8Bit().constData(), 1));
andre@304: #else
aheinecke@326:     {
aheinecke@326:         char buf[fakeHome.path().toLocal8Bit().size() + 9];
aheinecke@326:         snprintf(buf, fakeHome.path().toLocal8Bit().size() + 9,
aheinecke@326:                 "APPDATA=%s",fakeHome.path().toLocal8Bit().constData());
aheinecke@326:         QVERIFY(_putenv (buf) != -1);
aheinecke@326:     }
andre@304:     fakeFirefoxDir = QDir(fakeHome.path() + "/Mozilla/firefox");
andre@304:     fakeThunderbirdDir = QDir(fakeHome.path() + "/Thunderbird");
andre@304: #endif
andre@304:     QVERIFY(fakeFirefoxDir.mkpath(fakeFirefoxDir.path()));
andre@304:     QVERIFY(fakeThunderbirdDir.mkpath(fakeThunderbirdDir.path()));
andre@304: 
andre@304:     QFile mozProfile(fakeFirefoxDir.absoluteFilePath("profiles.ini"));
andre@304:     QFile tbProfile(fakeThunderbirdDir.absoluteFilePath("profiles.ini"));
andre@304: 
andre@304:     /* Write profiles */
andre@304:     QVERIFY(mozProfile.open(QIODevice::WriteOnly));
andre@304:     QTextStream ffStream(&mozProfile);
andre@304:     ffStream << endl << "[General]"<<
andre@304:         "StartWithLastProfile=1" << endl <<
andre@304:         "[Profile0]" << endl <<
andre@304:         "Name=default" << endl <<
andre@304:         "IsRelative=1" << endl <<
andre@307:         "Path=" << fakeFirefoxDir.relativeFilePath(ffNSSDir.path()) << endl;
andre@304:     ffStream.flush();
andre@304:     mozProfile.close();
andre@304: 
andre@304:     QVERIFY(tbProfile.open(QIODevice::WriteOnly));
andre@304:     QTextStream tbStream(&tbProfile);
andre@304:     tbStream << endl << "[General]"<<
andre@304:         "StartWithLastProfile=1" << endl <<
andre@304:         "[Profile102]" << endl <<
andre@304:         "Name=default" << endl <<
andre@304:         "IsRelative=0" << endl <<
andre@307:         "Path=" << tbNSSDir.path() << endl;
andre@304:     tbStream.flush();
andre@304:     tbProfile.close();
andre@304: }
andre@304: 
andre@304: void NSSTest::testInstRemove() {
andre@304:     char ** to_install = NULL,
andre@304:          ** to_remove = NULL;
andre@304: 
andre@304:     QList<Certificate> instList;
andre@304: 
andre@307:     /* Install all certificates */
andre@304:     foreach (const Certificate &cert, validList.getCertificates()) {
andre@304:         if (!cert.isInstallCert())
andre@304:             continue;
andre@304:         instList << cert;
andre@304:         strv_append (&to_install, cert.base64Line().toLatin1().constData() + 2,
andre@304:                 cert.base64Line().size() - 2);
andre@304:     }
andre@304:     QVERIFY((size_t) instList.size() == strv_length(to_install));
andre@371:     QVERIFY(strv_length(to_install) != 0);
andre@304: 
andre@305:     QVERIFY(write_stores_nss(to_install, to_remove) == 0);
andre@305: 
andre@307:     {
andre@307:         /* Verify that everything is installed */
andre@307:         QList<QByteArray> installedCertsFF = get_nss_certs(&ffNSSDir);
andre@307:         QList<QByteArray> installedCertsTB = get_nss_certs(&tbNSSDir);
andre@307: 
andre@307:         QVERIFY(installedCertsFF.size() == instList.size());
andre@307:         QVERIFY(installedCertsFF == installedCertsTB);
andre@307: 
andre@307:         for (int i = 0; to_install[i]; i++) {
andre@307:             QByteArray bai = QByteArray::fromBase64(to_install[i]);
andre@307:             QVERIFY(installedCertsFF.contains(bai));
andre@307:         }
andre@307:     }
andre@307: 
andre@307:     {
andre@307:         /* Remove one certificate */
andre@307:         QVERIFY(instList.size() > 2);
andre@307:         strv_append (&to_remove, to_install[1], qstrlen(to_install[1]));
andre@307: 
aheinecke@477:         QList<QByteArray> beforeFF = get_nss_certs(&ffNSSDir);
aheinecke@477:         QVERIFY(strv_length(to_remove) == 1);
andre@307:         QVERIFY(write_stores_nss(NULL, to_remove) == 0);
andre@307: 
andre@307:         QList<QByteArray> installedCertsFF = get_nss_certs(&ffNSSDir);
andre@307:         QList<QByteArray> installedCertsTB = get_nss_certs(&tbNSSDir);
aheinecke@477:         QVERIFY(beforeFF.size() > 0);
aheinecke@477:         QVERIFY(beforeFF.size() - 1 == installedCertsFF.size());
andre@307: 
andre@307:         QVERIFY(installedCertsFF == installedCertsTB);
andre@307: 
andre@307:         QByteArray bai = QByteArray::fromBase64(to_install[1]);
andre@307:         QVERIFY(!installedCertsTB.contains(bai));
andre@307: 
andre@307:         QVERIFY((size_t)installedCertsTB.size() == strv_length(to_install) - 1);
andre@307: 
andre@307:         for (int i = 0; to_install[i]; i++) {
andre@307:             if (i == 1) {
andre@307:                 continue;
andre@307:             }
andre@307:             QByteArray bai = QByteArray::fromBase64(to_install[i]);
andre@307:             QVERIFY(installedCertsTB.contains(bai));
andre@307:         }
andre@307:     }
andre@307: 
andre@307:     {
andre@307:         /* Readd all certificates check for duplication*/
andre@307:         QVERIFY(write_stores_nss(to_install, NULL) == 0);
andre@307: 
andre@307:         QList<QByteArray> installedCertsFF = get_nss_certs(&ffNSSDir);
andre@307:         QList<QByteArray> installedCertsTB = get_nss_certs(&tbNSSDir);
andre@307: 
andre@307:         QVERIFY(installedCertsFF == installedCertsTB);
andre@307: 
andre@307:         QVERIFY((size_t)installedCertsTB.size() == strv_length(to_install));
andre@307: 
andre@307:         for (int i = 0; to_install[i]; i++) {
andre@307:             QByteArray bai = QByteArray::fromBase64(to_install[i]);
andre@307:             QVERIFY(installedCertsTB.contains(bai));
andre@307:         }
andre@307:     }
andre@307: 
andre@307:     {
andre@307:         /* Remove all certificates */
andre@307:         QVERIFY(write_stores_nss(NULL, to_install) == 0);
andre@307: 
andre@307:         QList<QByteArray> installedCertsFF = get_nss_certs(&ffNSSDir);
andre@307:         QList<QByteArray> installedCertsTB = get_nss_certs(&tbNSSDir);
andre@307: 
andre@307:         QVERIFY(installedCertsFF == installedCertsTB);
andre@307: 
andre@307:         QVERIFY(installedCertsTB.size() == 0);
andre@307:     }
andre@304: }
andre@304: 
aheinecke@492: void NSSTest::benchmarkInstall()
aheinecke@492: {
aheinecke@492:     char ** to_install = NULL,
aheinecke@492:          ** to_remove = NULL;
aheinecke@492: 
aheinecke@492:     QList<Certificate> instList;
aheinecke@492: 
aheinecke@492:     /* Install all certificates */
aheinecke@492:     foreach (const Certificate &cert, validList.getCertificates()) {
aheinecke@492:         if (!cert.isInstallCert())
aheinecke@492:             continue;
aheinecke@492:         instList << cert;
aheinecke@492:         strv_append (&to_install, cert.base64Line().toLatin1().constData() + 2,
aheinecke@492:                 cert.base64Line().size() - 2);
aheinecke@492:     }
aheinecke@492:     QVERIFY((size_t) instList.size() == strv_length(to_install));
aheinecke@492:     QVERIFY(strv_length(to_install) != 0);
aheinecke@492: 
aheinecke@534:     QBENCHMARK_ONCE {
aheinecke@492:         write_stores_nss(to_install, to_remove);
aheinecke@492:     }
aheinecke@492: }
andre@1060: 
andre@1060: bool g_debug = true;
andre@1060: 
andre@304: QTEST_GUILESS_MAIN (NSSTest);