# HG changeset patch # User Sascha Wilde # Date 1396598059 -7200 # Node ID f758460ca43736b7b0bdff0057d2127aed5b8312 # Parent fa37384b86b6404135fa6700d83dbf3685059005# Parent 2fd69803d2199668117cf55e42a7855a2b9d3fb7 Merged diff -r fa37384b86b6 -r f758460ca437 CMakeLists.txt --- a/CMakeLists.txt Fri Apr 04 09:53:55 2014 +0200 +++ b/CMakeLists.txt Fri Apr 04 09:54:19 2014 +0200 @@ -20,6 +20,8 @@ find_package(Qt5Widgets) +find_package(NSS) + # Use cmake's automoc and make sure the generated files are included set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff -r fa37384b86b6 -r f758460ca437 build.sh --- a/build.sh Fri Apr 04 09:53:55 2014 +0200 +++ b/build.sh Fri Apr 04 09:54:19 2014 +0200 @@ -8,7 +8,7 @@ cd `dirname $0` # build requirements need to be installed: -# polarssl, qt5, (opt) doxygen, (opt) hiawatha-webserver +# polarssl, qt5, (opt) doxygen, (opt) hiawatha-webserver, (opt) libnss3-dev mkdir -p build-linux cd build-linux diff -r fa37384b86b6 -r f758460ca437 cinst/CMakeLists.txt --- a/cinst/CMakeLists.txt Fri Apr 04 09:53:55 2014 +0200 +++ b/cinst/CMakeLists.txt Fri Apr 04 09:54:19 2014 +0200 @@ -6,6 +6,7 @@ set(CINST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/windowsstore.c + ${CMAKE_CURRENT_SOURCE_DIR}/nssstore_linux.c ${CMAKE_CURRENT_SOURCE_DIR}/main.c ) add_executable(cinst ${CINST_SOURCES}) @@ -25,8 +26,6 @@ # ---------------------------------------------------------------------- # Mozilla nss store specific certificate installer: -find_package(NSS) - if(NSS_FOUND) include_directories(${NSS_INCLUDE_DIRS}) set(MOZILLA_SOURCES diff -r fa37384b86b6 -r f758460ca437 cinst/main.c --- a/cinst/main.c Fri Apr 04 09:53:55 2014 +0200 +++ b/cinst/main.c Fri Apr 04 09:54:19 2014 +0200 @@ -7,23 +7,26 @@ * process will modify system wide certificate stores. * Otherwise only the users certificate stores are modified. * - * It expects a certificatelist on stdin enclosed in a - * -----BEGIN CERTIFICATE LIST----- - * ... - * -----END CERTIFICATE LIST----- + * The first parameter to this process should be list= + * of the certificate list to work on. The second parameter should + * be instruction=|uninstall * - * Followed by additional instruction lines of: + * instruction_file_name should be the absolute path to an + * instructions file formatted as: + * * I: * R: * + * Line breaks can be system dependent in the Instructions file. + * * It will only execute the instructions if the * I and R instructions are also part of the signed * certificate list. The signature is validated with the * built in key. * - * The special instruction "UNINSTALL" will cause the installer + * The special instruction "uninstall" will cause the installer * to remove all certificates (Even those marked with I) that - * are part of the list to be removed. + * are part of the list. * **/ #include @@ -34,8 +37,10 @@ #include "strhelp.h" #include "listutil.h" +#include "logging.h" #include "errorcodes.h" #include "windowsstore.h" +#include "nssstore.h" /* The certificate list + instructions may only be so long as * twice the accepted certificatelist size */ @@ -43,40 +48,62 @@ /* @brief Read stdin into data structures. * - * Reads from stdin and sorts the input into the respective - * variables. The pointers returned need to be freed by the caller. + * Reads instructions from an input file into the to_install + * and to_remove buffers. + * + * Lines starting with I: are treated as install instructions. + * Lines starting with R: are treated as remove instructions. + * Other lines are ignored. + * * Terminates in OOM conditions. * * The caller needs to free the memory allocated by this function * even when an error is returned. * - * Uninstall certificates are all certificates that are pa - * - * @param[out] certificate_list the parsed certificate list + * @param[in] file_name absolute path to the instructions file. * @param[out] to_install strv of installation instructions or NULL * @param[out] to_remove strv of remove instructions or NULL - * @param[out] all_certs strv of uninstallation instructions or NULL * * @returns: 0 on success. An error code otherwise. */ -int -readInput (char **certificate_list, char ***to_install, - char ***to_remove, char ***all_certs) +static int +read_instructions_file (char *file_name, char ***to_install, + char ***to_remove) { int lines_read = 0; - int readingList = 0; - size_t list_size = 0; char buf[MAX_LINE_LENGTH + 2]; + FILE *f = NULL; + long file_size; - if (*certificate_list || *to_install || *to_remove) + if (*to_install || *to_remove) { - printf ("Error invalid parameters\n"); + printf ("Error invalid parameters.\n"); return -1; } - while (fgets (buf, MAX_LINE_LENGTH + 1, stdin) ) + f = fopen (file_name, "rb"); + if (f == NULL) + return ERR_NO_INSTRUCTIONS; + + fseek (f, 0, SEEK_END); + file_size = ftell (f); + if (file_size <= 0) { - size_t len = strlen (buf); /* fgets ensures buf is terminated */ + fclose (f); + return ERR_NO_INSTRUCTIONS; + } + + fseek (f, 0, SEEK_SET); + + if (file_size + 1 == 0) + { + fclose (f); + return ERR_INVALID_INSTRUCTIONS; + } + + while (fgets (buf, MAX_LINE_LENGTH + 1, f) ) + { + size_t len = strlen (buf); /* fgets ensures buf is terminated */ if (len <= 3) { printf ("Line too short.\n"); @@ -87,50 +114,20 @@ printf ("Too many lines\n"); return ERR_TOO_MUCH_INPUT; } - - if (buf[len - 2] != '\r') - { - if (buf[len - 1] != '\n') - { - printf ("Line too long.\n"); - return ERR_INVALID_INPUT; - } - buf[len - 1] = '\r'; - buf[len] = '\n'; - buf[len + 1] = '\0'; - len++; - } - - if (strcmp ("-----BEGIN CERTIFICATE LIST-----\r\n", buf) == 0) - { - readingList = 1; - continue; - } - if (strcmp ("-----END CERTIFICATE LIST-----\r\n", buf) == 0) - { - readingList = 0; - continue; - } - if (readingList) - { - str_append_str (certificate_list, &list_size, buf, len); - } - else if (strcmp ("UNINSTALL\r\n", buf) == 0) - { - /* Remove trailing \r\n */ - strv_append (to_remove, buf, len - 2); - continue; - } if (*buf == 'I') { - /* Remove leading I: and trailing \r\n */ - strv_append (readingList ? all_certs : to_install, buf + 2, len - 4); + char *trimmed = buf+2; + /* Remove leading I: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_install, trimmed, strlen(trimmed)); continue; } if (*buf == 'R') { - /* Remove leading R: and trailing \r\n */ - strv_append (readingList ? all_certs : to_remove, buf + 2, len - 4); + char *trimmed = buf+2; + /* Remove leading R: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_remove, trimmed, strlen(trimmed)); continue; } } @@ -170,7 +167,7 @@ bool found = false; for (j = 0; all_certs[j]; j++) { - if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH - 2) == + if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH) == 0) { found = true; @@ -179,7 +176,7 @@ } if (!found) { - printf ("Install instruction with invalid certificate\n."); + DEBUGPRINTF ("Failed to find certificate; \n%s\n", to_validate[i]); return ERR_INVALID_INSTRUCTIONS; } } @@ -189,47 +186,103 @@ int -main () +main (int argc, char **argv) { - char **to_install = NULL; - char **to_remove = NULL; - char **all_certs = NULL; - char *certificate_list = NULL; + /* TODO handle wchar arguments on Windows or do conversion dance */ + char **to_install = NULL, + **to_remove = NULL, + **all_valid_certs = NULL; + int ret = -1; + + char *certificate_list = NULL, + *certificate_file_name = NULL, + *instruction_file_name = NULL; size_t list_len = 0; - int ret = -1; - bool uninstall = false; + list_status_t list_status; + bool do_uninstall = false; - ret = readInput (&certificate_list, &to_install, &to_remove, &all_certs); + /* Some very static argument parsing. list= and instructions= is only + added to make it more transparent how this programm is called if + a user looks at the detailed uac dialog. */ + if (argc != 3 || strncmp(argv[1], "list=", 5) != 0 || + strncmp(argv[2], "instructions=", 13) != 0) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list= \n" + " instructions=|uninstall\n"); + return ERR_INVALID_PARAMS; + } + + certificate_file_name = strchr(argv[1], '=') + 1; + instruction_file_name = strchr(argv[2], '=') + 1; + + if (!certificate_file_name || !instruction_file_name) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list= \n" + " instructions=|uninstall\n"); + return ERR_INVALID_PARAMS; + } + + if (strncmp(instruction_file_name, "uninstall", 9) == 0) + { + do_uninstall = true; + instruction_file_name = NULL; + } + + list_status = read_and_verify_list (certificate_file_name, &certificate_list, + &list_len); + + if (list_status != Valid) + { + if (list_status == InvalidSignature) + { + return ERR_INVALID_SIGNATURE; + } + + return ERR_INVALID_INPUT_NO_LIST; + } + + all_valid_certs = get_certs_from_list (certificate_list, list_len); + + if (!all_valid_certs) + { + /* Impossible */ + return -1; + } + + + /* For uninstall we are done now */ + if (do_uninstall) + { +#ifdef WIN32 + ret = write_stores_win (NULL, all_valid_certs); + if (ret != 0) + { + ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); + } +#endif + ret = write_stores_nss (NULL, all_valid_certs); + return ret; + } + + ret = read_instructions_file (instruction_file_name, &to_install, + &to_remove); if (ret) { return ret; } - if (!certificate_list) - { - return ERR_INVALID_INPUT_NO_LIST; - } - - list_len = strnlen (certificate_list, MAX_INPUT_SIZE); - - ret = verify_list (certificate_list, list_len); - - if (ret) - { - return ERR_INVALID_SIGNATURE; - } - if (!strv_length (to_install) && !strv_length (to_remove) ) { return ERR_NO_INSTRUCTIONS; } - /* Check that the instructions are ok to execute */ if (to_install) { - ret = validate_instructions (all_certs, to_install); + ret = validate_instructions (all_valid_certs, to_install); if (ret) { return ret; @@ -238,39 +291,27 @@ if (to_remove) { - if (to_remove[0] - && strncmp ("UNINSTALL", to_remove[0], MAX_LINE_LENGTH) == 0) + ret = validate_instructions (all_valid_certs, to_remove); + if (ret) { - uninstall = true; - strv_free (to_remove); - to_remove = NULL; - } - else - { - ret = validate_instructions (all_certs, to_remove); - if (ret) - { - return ret; - } + return ret; } } - if (uninstall) +#ifdef WIN32 + ret = write_stores_win (to_install, to_remove); + if (ret != 0) { - /* To uninstall does not have to be verified as it part of the - * signed list.*/ - to_remove = all_certs; + ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); } - else +#endif + ret = write_stores_nss (to_install, to_remove); + + if (ret != 0) { - strv_free (all_certs); - all_certs = NULL; + ERRORPRINTF ("Failed to write nss stores"); } -#ifdef WIN32 - return write_stores_win (to_install, to_remove); -#endif - /* Make valgrind happy */ strv_free (to_install); strv_free (to_remove); diff -r fa37384b86b6 -r f758460ca437 cinst/nssstore.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/nssstore.h Fri Apr 04 09:54:19 2014 +0200 @@ -0,0 +1,27 @@ +#ifndef NSSSTORE_H +#define NSSSTORE_H +/** @file + * @brief Helper functions controlling the NSS installation process. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Write into NSS stores + * + * Starts the nss installation process for all users + * we have the right to impersonate and installs / removes + * the certificates in their stores. + + * @param [in] to_install strv of DER encoded certificates to be added. + * @param [in] to_remove strv of DER encoded certificates to be remvoed. + * @returns 0 on success an errorcode otherwise. + */ +int write_stores_nss (char **to_install, char **to_remove); + +#ifdef __cplusplus +} +#endif + +#endif // NSSSTORE_H diff -r fa37384b86b6 -r f758460ca437 cinst/nssstore_linux.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/nssstore_linux.c Fri Apr 04 09:54:19 2014 +0200 @@ -0,0 +1,178 @@ +#ifndef WIN32 + +#include +#include +#include +#include +#include +#include +#include + +#include "nssstore.h" +#include "logging.h" +#include "strhelp.h" + +/**@brief Start the process to install / remove + * + * This forks the process and executes the NSS installation + * process. It also writes the Instructions to that process. + * + * @param [in] to_install strv of DER encoded certificates to be added. + * @param [in] to_remove strv of DER encoded certificates to be remvoed. + * @param [in] uid_t uid of the user to install certificates for. + * @param [in] gid_t the gid of the user to install certificates for. + * @param [in] homedir the homedir of the user. + * + * @returns childs pid on success. -1 on failure + */ +static int +start_procces_for_user (char **to_install, char **to_remove, + uid_t uid, gid_t gid, char *homedir) +{ + int pipe_fd[2]; + pid_t pid = 0; + char *argv[] = {"mozilla", NULL}, + *envp[2]; + size_t homedir_len = 0; + int ret = -1, + i = 0; + FILE *stream = NULL; + bool success = false; + + if (homedir == NULL) + { + ERRORPRINTF ("Invalid call to start_process_for_user\n"); + return -1; + } + + homedir_len = strlen (homedir); + + /* Allocate space for HOME=homedir\0 */ + envp[0] = xmalloc (homedir_len + 6); + envp[1] = NULL; + + ret = snprintf (envp[0], homedir_len + 6, "HOME=%s", homedir); + + if (ret < 0 || (size_t) ret != homedir_len + 5) + { + ERRORPRINTF ("Error setting home env variable.\n"); + xfree (envp[0]); + return -1; + } + + DEBUGPRINTF ("Home: %s \n", envp[0]); + + if (pipe (pipe_fd)) + { + ERRORPRINTF ("Failed to create pipe.\n"); + return -1; + } + + pid = fork(); + + if (pid == (pid_t) -1) + { + ERRORPRINTF ("Failed to fork child.\n"); + return -1; + } + + if (pid == (pid_t) 0) + { + /* Drop privileges */ + if (setuid (uid) || setgid (gid)) + { + exit(-1); + } + + close (pipe_fd[1]); + dup2 (pipe_fd[0], 0); + close (pipe_fd[0]); + /* TODO find path based on current executable */ + execve ("mozilla", argv, envp); + exit (127); + } + + close (pipe_fd[0]); + stream = fdopen(pipe_fd[1], "w"); + if (stream == NULL) + { + ERRORPRINTF ("Failed to open pipe for writing\n"); + goto done; + } + + /* Send the instructions */ + for (i = 0; to_install && to_install[i]; i++) + { + if (fprintf (stream, "I:%s\n", to_install[i]) <= 3) + { + ERRORPRINTF ("Write failed \n"); + goto done; + } + } + + for (i = 0; to_remove && to_remove[i]; i++) + { + if (fprintf (stream, "R:%s\n", to_remove[i]) <= 3) + { + ERRORPRINTF ("Write failed \n"); + goto done; + } + } + + success = true; + +done: + if (stream) { + fclose (stream); + } + xfree (envp[0]); + close(pipe_fd[0]); + close(pipe_fd[1]); + + if (success) + { + return pid; + } + return -1; +} + +int +write_stores_nss (char **to_install, char **to_remove) +{ + uid_t my_uid = getuid(); + + if (my_uid != 0) + { + /* Running as a user */ + char *homedir = getenv ("HOME"); + pid_t childprocess = -1; /* Only one child for single user installation */ + int status = -1; + if (!homedir) + { + ERRORPRINTF ("Failed to find home directory\n"); + } + + childprocess = start_procces_for_user (to_install, to_remove, + my_uid, getgid(), homedir); + + if (childprocess == -1) + { + ERRORPRINTF ("Failed to start childprocess!\n"); + return -1; + } + + childprocess = waitpid (childprocess, &status, 0); + if (childprocess == -1 || !WIFEXITED(status)) + { + ERRORPRINTF ("Waitpid failed.\n"); + return -1; + } + DEBUGPRINTF ("Child returned status: %i\n", WEXITSTATUS(status)); + + return 0; + } + printf ("Installation as root is not yet implemented\n"); + /* TODO root parse /etc/passwd for users with a home directory */ + return 0; +} +#endif diff -r fa37384b86b6 -r f758460ca437 common/errorcodes.h --- a/common/errorcodes.h Fri Apr 04 09:53:55 2014 +0200 +++ b/common/errorcodes.h Fri Apr 04 09:54:19 2014 +0200 @@ -3,7 +3,7 @@ /* No error */ #define ERR_NO_ERROR 0 -/* No begin certificate / end certificate could be found */ +/* Failed to read / verify the certificate list */ #define ERR_INVALID_INPUT_NO_LIST 2 /* Too much input for the installer process */ #define ERR_TOO_MUCH_INPUT 3 @@ -21,6 +21,8 @@ #define ERR_INVALID_INPUT 9 /* Generic invalid certificate */ #define ERR_INVALID_CERT 10 +/* Invalid parameters in call */ +#define ERR_INVALID_PARAMS 11 /*********************************************************************** * mozilla specific errors and warnings diff -r fa37384b86b6 -r f758460ca437 common/listutil.c --- a/common/listutil.c Fri Apr 04 09:53:55 2014 +0200 +++ b/common/listutil.c Fri Apr 04 09:54:19 2014 +0200 @@ -9,6 +9,8 @@ #include #include +#include "strhelp.h" + #ifdef RELEASE #include "pubkey-release.h" #else @@ -232,22 +234,30 @@ return retval; } -char **get_certs_to_remove(const char *data, const size_t size) { +char ** +get_certs_from_list (char *data, const size_t size) +{ + char *cur = data; + char **retval = NULL; - /* TODO */ - if (!data || !size) { - printf ("Invalid call to get_certs_to_remove \n"); - return NULL; + if (!data || !size) + { + printf ("Invalid call to get_certs_to_remove \n"); + return NULL; } - return NULL; + + while (cur) + { + char *next = strchr(cur, '\n'); + if (strlen(cur) > 3 && (cur[0] == 'I' || cur[0] == 'R') && + next - cur > 4) + { + size_t len = (size_t) (next - cur - 3); + /* Remove I: or R: at the beginning and \r\n at the end */ + strv_append(&retval, cur + 2, len); + } + cur = next ? (next + 1) : NULL; + } + return retval; } -char **get_certs_to_install(const char *data, const size_t size) { - - /* TODO */ - if (!data || !size) { - printf ("Invalid call to get_certs_to_install \n"); - return NULL; - } - return NULL; -} diff -r fa37384b86b6 -r f758460ca437 common/listutil.h --- a/common/listutil.h Fri Apr 04 09:53:55 2014 +0200 +++ b/common/listutil.h Fri Apr 04 09:54:19 2014 +0200 @@ -59,9 +59,9 @@ */ int verify_list(const char *data, const size_t size); -/** @brief get a list of the certificates marked with I: +/** @brief get a list of the certificates marked with I: or R: * - * Get a list of certificates that should be installed by the + * Get a list of certificates that are contained in the * certificatelist pointed to by data. * On Success this function makes a copy of the certificates * and the certificates need to be freed by the caller. @@ -72,23 +72,7 @@ * @returns a newly allocated array of strings containing the encoded * certificates or NULL on error. * */ -char **get_certs_to_install(const char *data, const size_t size); - -/** @brief get a list of the certificates marked with R: - * - * Get a list of certificates that should be removed by the - * certificatelist pointed to by data. - * On Success this function makes a copy of the certificates - * and the certificates need to be freed by the caller. - * - * @param [in] data the certificatelist to parse - * @param [in] size the size of the certificatelist - * - * @returns a newly allocated array of strings containing the encoded - * certificates or NULL on error. - * */ -char **get_certs_to_remove(const char *data, const size_t size); - +char **get_certs_from_list (char *data, const size_t size); #ifdef __cplusplus } diff -r fa37384b86b6 -r f758460ca437 common/logging.h --- a/common/logging.h Fri Apr 04 09:53:55 2014 +0200 +++ b/common/logging.h Fri Apr 04 09:54:19 2014 +0200 @@ -56,6 +56,34 @@ #define DEBUGPRINTF(fmt, ...) #endif +/** + * @def DEBUGMSG(msg) + * @brief Prints a static debug message + * + * If DEBUGOUTPUT is defined this. Prints a debug message + * to stdout on Unix systems. On Windows OutputDebugString is used. + */ +#ifdef DEBUGOUTPUT +# ifdef WIN32 +# define DEBUGMSG(msg) OutputDebugString (msg); +# else +# define DEBUGMSG(msg) printf (DEBUGPREFIX "DEBUG: " msg "\n"); +# endif +#else +# define DEBUGPRINTF(fmt, ...) +#endif + + +/** + * @def ERRORPRINTF(fmt, ...) + * @brief Debug printf + * + * Prints an error to stderr + */ +#define ERRORPRINTF(fmt, ...) fprintf(stderr, DEBUGPREFIX "ERROR: " fmt, ##__VA_ARGS__); + + + #ifdef __cplusplus } #endif diff -r fa37384b86b6 -r f758460ca437 common/strhelp.c --- a/common/strhelp.c Fri Apr 04 09:53:55 2014 +0200 +++ b/common/strhelp.c Fri Apr 04 09:54:19 2014 +0200 @@ -219,6 +219,7 @@ xfree (result); return NULL; } + result[n] = 0; return result; } diff -r fa37384b86b6 -r f758460ca437 ui/installwrapper.cpp --- a/ui/installwrapper.cpp Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/installwrapper.cpp Fri Apr 04 09:54:19 2014 +0200 @@ -8,6 +8,8 @@ #include "logging.h" +#define INSTALL_TIMEOUT 3600000 /* Wait up to an hour */ + InstallWrapper::InstallWrapper(QObject* parent, const QString& path, const QStringList& instructions): QThread(parent), @@ -27,32 +29,26 @@ } #ifdef WIN32 -extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; - void InstallWrapper::run() { + /* TODO: We need errorcodes here so that we can see if a user + * cancled the UAC elevation */ QTemporaryFile instructionsFile; QFileInfo cinstProcInfo = getCinstProcInfo(); + DWORD retval = 0; + SHELLEXECUTEINFOW shExecInfo; + memset (&shExecInfo, 0, sizeof(SHELLEXECUTEINFOW)); QString cinstFileName = QDir::toNativeSeparators( getCinstProcInfo().absoluteFilePath()); if (!cinstProcInfo.isExecutable()) { - emit error (tr("Could not find certificate installation process.")); + emit error(tr("Could not find certificate installation process.")); return; } instructionsFile.open(); - qt_ntfs_permission_lookup++; - if (instructionsFile.permissions() ^ ( - QFileDevice::ReadUser | - QFileDevice::WriteUser | - QFileDevice::ReadOwner | - QFileDevice::WriteOwner)) { - emit error (tr("Invalid permissions on temporary file.")); - } - foreach (const QString &b64data, mInstructions) { instructionsFile.write(b64data.toLatin1()); instructionsFile.write("\n"); @@ -60,25 +56,56 @@ instructionsFile.close(); - QString parameters = "\"" + mCertListFile + "\" \"" +instructionsFile.fileName() + "\""; + QString parameters = "\"list=" + mCertListFile + + "\" \"instructions=" +instructionsFile.fileName() + "\""; - memset (&mExecInfo, 0, sizeof(SHELLEXECUTEINFOW)); - mExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); - mExecInfo.fMask = SEE_MASK_FLAG_NO_UI | - SEE_MASK_NOASYNC; - mExecInfo.lpVerb = L"runas"; - mExecInfo.lpFile = reinterpret_cast (cinstFileName.utf16()); - mExecInfo.lpParameters = reinterpret_cast (parameters.utf16()); + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); + shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + shExecInfo.lpVerb = L"runas"; + shExecInfo.lpFile = reinterpret_cast (cinstFileName.utf16()); + shExecInfo.lpParameters = reinterpret_cast (parameters.utf16()); qDebug() << "Starting process " << cinstFileName <<" params: " << parameters; - if (!ShellExecuteExW(&mExecInfo)) { + if (!ShellExecuteExW(&shExecInfo)) { char* errmsg = getLastErrorMsg(); QString qerrmsg = QString::fromUtf8(errmsg); free(errmsg); - emit(tr("Error executing process: %1").arg(qerrmsg)); + emit error(tr("Error executing process: %1").arg(qerrmsg)); + return; } - qt_ntfs_permission_lookup--; + + retval = WaitForSingleObject(shExecInfo.hProcess, INSTALL_TIMEOUT); + + 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)); + return; + } else { + emit error (tr("Certificate installation timed out.")); + 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); + + if (retval != 0) { + /* TODO make this nicer */ + emit error (tr("The process failed with return code. %1").arg(retval)); + } } #else void InstallWrapper::run() diff -r fa37384b86b6 -r f758460ca437 ui/installwrapper.h --- a/ui/installwrapper.h Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/installwrapper.h Fri Apr 04 09:54:19 2014 +0200 @@ -3,7 +3,6 @@ #include #include -#include #include #include "certificate.h" @@ -45,11 +44,6 @@ private: const QString mCertListFile; const QStringList mInstructions; -#ifdef WIN32 - SHELLEXECUTEINFOW mExecInfo; -#else - QProcess cinstProc; -#endif protected: void run(); diff -r fa37384b86b6 -r f758460ca437 ui/tests/CMakeLists.txt --- a/ui/tests/CMakeLists.txt Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/tests/CMakeLists.txt Fri Apr 04 09:54:19 2014 +0200 @@ -1,6 +1,9 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../../common) +include_directories(${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/ui + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/cinst) find_package(Qt5Test) include_directories(${Qt5Test_INCLUDE_DIRS}) @@ -15,7 +18,7 @@ macro(add_m13_test _source _additional_sources) set(_test ${_source}) get_filename_component(_name ${_source} NAME_WE) - set(_test_sources_with_resources ${_test} ${_additional_sources}) + set(_test_sources_with_resources ${_test} "${_additional_sources};${CMAKE_CURRENT_SOURCE_DIR}/common.cpp") qt5_add_resources(_test_sources_with_resources ${CMAKE_CURRENT_SOURCE_DIR}/data/testdata.qrc) add_executable(${_name} ${_test_sources_with_resources}) @@ -29,22 +32,30 @@ # Add the current source dir to the definition # so that it can be used in file names in the tests. add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") -add_m13_test(certlistparsertest.cpp "${CERTIFICATELIST_SOURCES};${CMAKE_CURRENT_SOURCE_DIR}/common.cpp") +add_m13_test(certlistparsertest.cpp "${CERTIFICATELIST_SOURCES}") # Downloader if (HIAWATHA_EXECUTABLE) set(DOWNLOADER_SOURCES_WITH_RESOURCES ${DOWNLOADER_SOURCES}) qt5_add_resources(DOWNLOADER_SOURCES_WITH_RESOURCES ${M13UI_RESOURCES}) - add_m13_test(downloadertest.cpp "${DOWNLOADER_SOURCES_WITH_RESOURCES};${CMAKE_CURRENT_SOURCE_DIR}/common.cpp") + add_m13_test(downloadertest.cpp "${DOWNLOADER_SOURCES_WITH_RESOURCES}") endif() # Cinstprocess add_m13_test(cinstprocesstest.cpp "${CERTIFICATELIST_SOURCES}") +add_dependencies(cinstprocesstest cinst) add_m13_test(commontest.cpp "") if (WIN32) add_m13_test(windowsstoretest.cpp "${CERTIFICATELIST_SOURCES};${CMAKE_SOURCE_DIR}/cinst/windowsstore.c") endif (WIN32) +if (NSS_FOUND) + include_directories(${NSS_INCLUDE_DIRS}) + add_m13_test(nsstest.cpp "${CERTIFICATELIST_SOURCES};${CMAKE_SOURCE_DIR}/cinst/nssstore_linux.c") + target_link_libraries(nsstest ${NSS_LIBRARIES}) + add_dependencies(nsstest mozilla) +endif() + #add_m13_test(${CMAKE_SOURCE_DIR}/ui/main.cpp "${M13UI_SOURCES}") diff -r fa37384b86b6 -r f758460ca437 ui/tests/cinstprocesstest.cpp --- a/ui/tests/cinstprocesstest.cpp Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/tests/cinstprocesstest.cpp Fri Apr 04 09:54:19 2014 +0200 @@ -1,17 +1,31 @@ #include "cinstprocesstest.h" #include "certificatelist.h" #include "errorcodes.h" +#include "common.h" #include #include #include #include -#define RELATIVE_CINST_PATH "../../cinst/cinst" +#define CINST_PATH_CANDIDATES "../../cinst/cinst" << \ + "cinst" << "../../cinst/cinst.exe" << "cinst.exe"; -QProcess *CinstProcessTest::startCinstProcess() { +QProcess *CinstProcessTest::startCinstProcess(const QStringList& args) { + QStringList cinstCandidates; + cinstCandidates << CINST_PATH_CANDIDATES; + QString processPath; + foreach (const QString& candidate, cinstCandidates) { + QFileInfo fi(candidate); + if (fi.isExecutable()) { + processPath = candidate; + break; + } + } + QProcess *installerProcess = new QProcess(); - installerProcess->setProgram(RELATIVE_CINST_PATH); + installerProcess->setArguments(args); + installerProcess->setProgram(processPath); installerProcess->start(); installerProcess->waitForStarted(); return installerProcess; @@ -22,6 +36,7 @@ qDebug() << "Stdout:" << proc->readAllStandardOutput(); \ qDebug() << "Stderr:" << proc->readAllStandardError(); \ qDebug() << "Exit code: " << proc->exitCode(); \ + qDebug() << "Exit status: " << proc->exitStatus(); \ } \ QVERIFY(x) @@ -34,104 +49,158 @@ } void CinstProcessTest::testValidInput() { - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); + QStringList args; + args << "list=" + validListFile.fileName(); - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - installerProcess->write(validList.rawData().toLatin1()); - installerProcess->write("-----END CERTIFICATE LIST-----\r\n"); + QTemporaryFile instructions; + instructions.open(); + foreach (const Certificate &cert, validList.getCertificates()) { + instructions.write(cert.base64Line().toLatin1()); + instructions.write("\n"); + } + instructions.close(); - foreach (const Certificate &cert, validList.getCertificates()) { - installerProcess->write(cert.base64Line().toLatin1()); - installerProcess->write("\r\n"); - } + args << "instructions=" + instructions.fileName(); + QProcess* installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_NO_ERROR); } void CinstProcessTest::initTestCase() { - QDir dataDir = QDir(SOURCE_DIR"/data/"); - QString fileName = dataDir.absoluteFilePath("list-valid-signed.txt"); - validList = CertificateList(fileName.toLocal8Bit().data()); + QFile valid(":/list-valid-signed.txt"); + valid.open(QIODevice::ReadOnly); + validListFile.open(); + validListFile.write(valid.readAll()); + valid.close(); + validListFile.close(); + validList = CertificateList(validListFile.fileName().toLocal8Bit().data()); + + QVERIFY(validList.isValid()); + + QFile invalid(":/list-invalid-signed.txt"); + invalid.open(QIODevice::ReadOnly); + invalidListFile.open(); + invalidListFile.write(invalid.readAll()); + invalid.close(); + invalidListFile.close(); + invalidList = CertificateList(invalidListFile.fileName().toLocal8Bit().data()); + + QVERIFY(!invalidList.isValid()); + + QFile other(":/list-valid-other-signature.txt"); + other.open(QIODevice::ReadOnly); + otherListFile.open(); + otherListFile.write(other.readAll()); + other.close(); + otherListFile.close(); + otherList = CertificateList(otherListFile.fileName().toLocal8Bit().data()); + + QVERIFY(!otherList.isValid()); + +/* Set HOME or APPDATA so that nss stores are not touched + * see nsstest for the real test of that code */ +#ifdef WIN32 + QVERIFY(!setenv ("APPDATA", fakeHome.path().toLocal8Bit().constData(), 1)); +#else + QVERIFY(!setenv ("HOME", fakeHome.path().toLocal8Bit().constData(), 1)); +#endif } void CinstProcessTest::testNoList() { /* No list */ - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - installerProcess->write("-----END CERTIFICATE LIST-----\r\n"); + QTemporaryFile emptyFile; + emptyFile.open(); + emptyFile.close(); + QStringList args; + args << "list=" + emptyFile.fileName(); + + QTemporaryFile instructions; + instructions.open(); foreach (const Certificate &cert, validList.getCertificates()) { - installerProcess->write(cert.base64Line().toLatin1()); - installerProcess->write("\r\n"); + instructions.write(cert.base64Line().toLatin1()); + instructions.write("\n"); } + instructions.close(); + + args << "instructions=" + instructions.fileName(); + + QProcess* installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST); } void CinstProcessTest::testGarbageInput() { - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); - /* Garbage */ - installerProcess = startCinstProcess(); - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - int retval=0; - int bytesWritten=0; - do { - char garbage[1030]; - for (int i = 0; i < 1030; i++) { - garbage[i] = (char) qrand() % 255; - } - retval = installerProcess->write(garbage, 1030); - bytesWritten += retval; - } while (retval != -1 && bytesWritten < 15 *1024 *1024 ); + QStringList args; - finishVerify(installerProcess, ERR_INVALID_INPUT); + QString garbage = getRandomDataFile(21*1024*1024); + args << "list=" + garbage; + + QTemporaryFile instructions; + instructions.open(); + foreach (const Certificate &cert, validList.getCertificates()) { + instructions.write(cert.base64Line().toLatin1()); + instructions.write("\n"); + } + instructions.close(); + + args << "instructions=" + instructions.fileName(); + + QProcess* installerProcess = startCinstProcess(args); + /* If the following failed there may be leftovers in /tmp */ + finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST); + QVERIFY(QFile::remove(garbage)); } void CinstProcessTest::testNoInput() { - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); - - /* Nothing */ - installerProcess = startCinstProcess(); + QStringList args; + args << "list=foobazbuf"; + args << "instructions=bazbuffoo"; + QProcess* installerProcess; + installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_INVALID_INPUT_NO_LIST); } void CinstProcessTest::testNoInstructions() { /* No instructions */ - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - installerProcess->write(validList.rawData().toLatin1()); - installerProcess->write("-----END CERTIFICATE LIST-----\r\n"); + QTemporaryFile emptyFile; + emptyFile.open(); + emptyFile.close(); + QStringList args; + args << "list=" + validListFile.fileName(); + args << "instructions=" + emptyFile.fileName(); + + QProcess* installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_NO_INSTRUCTIONS); } void CinstProcessTest::testInvalidInstruction() { - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); + QStringList args; + args << "list=" + validListFile.fileName(); - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - installerProcess->write(validList.rawData().toLatin1()); - installerProcess->write("-----END CERTIFICATE LIST-----\r\n"); + QTemporaryFile instructions; + instructions.open(); + foreach (const Certificate &cert, validList.getCertificates()) { + instructions.write(cert.base64Line().toLatin1()); + instructions.write("\n"); + } + instructions.write("I:ABCDEF\n"); + instructions.close(); - installerProcess->write("I:ABCDEF\r\n"); + args << "instructions=" + instructions.fileName(); + + QProcess* installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_INVALID_INSTRUCTIONS); } void CinstProcessTest::testUninstall() { - QProcess* installerProcess = startCinstProcess(); - QVERIFY(installerProcess->state() == QProcess::Running); + QStringList args; + args << "list=" + validListFile.fileName(); + args << "instructions=uninstall"; - installerProcess->write("-----BEGIN CERTIFICATE LIST-----\r\n"); - installerProcess->write(validList.rawData().toLatin1()); - installerProcess->write("-----END CERTIFICATE LIST-----\r\n"); - - installerProcess->write("UNINSTALL\r\n"); + QProcess* installerProcess = startCinstProcess(args); finishVerify(installerProcess, ERR_NO_ERROR); } diff -r fa37384b86b6 -r f758460ca437 ui/tests/cinstprocesstest.h --- a/ui/tests/cinstprocesstest.h Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/tests/cinstprocesstest.h Fri Apr 04 09:54:19 2014 +0200 @@ -6,7 +6,10 @@ */ #include +#include #include +#include +#include #include #include "certificatelist.h" @@ -15,7 +18,11 @@ Q_OBJECT private: - QProcess* startCinstProcess(); + QProcess* startCinstProcess(const QStringList& args = QStringList()); + QTemporaryFile validListFile; + QTemporaryFile otherListFile; + QTemporaryFile invalidListFile; + QTemporaryDir fakeHome; CertificateList validList; CertificateList otherList; CertificateList invalidList; diff -r fa37384b86b6 -r f758460ca437 ui/tests/data/NOTES --- a/ui/tests/data/NOTES Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/tests/data/NOTES Fri Apr 04 09:54:19 2014 +0200 @@ -60,3 +60,8 @@ CERT=$(cat valid_ssl_rsa.pem | grep -v "\-\-\-\-" | tr -d "\\n") echo -e R:${CERT}\\r >> list-valid.txt done + +# NSS +mkdir nss +certutil -d nss -A -i valid_ssl_rsa.pem -n "test" -t c,C +certutil -d nss -D -n "test" diff -r fa37384b86b6 -r f758460ca437 ui/tests/data/nss/cert8.db Binary file ui/tests/data/nss/cert8.db has changed diff -r fa37384b86b6 -r f758460ca437 ui/tests/data/nss/key3.db Binary file ui/tests/data/nss/key3.db has changed diff -r fa37384b86b6 -r f758460ca437 ui/tests/data/nss/secmod.db Binary file ui/tests/data/nss/secmod.db has changed diff -r fa37384b86b6 -r f758460ca437 ui/tests/data/testdata.qrc --- a/ui/tests/data/testdata.qrc Fri Apr 04 09:53:55 2014 +0200 +++ b/ui/tests/data/testdata.qrc Fri Apr 04 09:54:19 2014 +0200 @@ -1,6 +1,11 @@ list-valid-signed.txt + list-invalid-signed.txt + list-valid-other-signature.txt + nss/cert8.db + nss/key3.db + nss/secmod.db diff -r fa37384b86b6 -r f758460ca437 ui/tests/nsstest.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/tests/nsstest.cpp Fri Apr 04 09:54:19 2014 +0200 @@ -0,0 +1,197 @@ +#include +#include +#include + +#include +#include + +#include "nsstest.h" +#include "nssstore.h" +#include "strhelp.h" + +#include + +QList NSSTest::get_nss_certs (QTemporaryDir *nssDir) +{ + CERTCertList *list; + CERTCertListNode *node; + QList retval; + + if (NSS_Initialize(nssDir->path().toLocal8Bit().constData(), + "", "", "secmod.db", NSS_INIT_READONLY) + == SECSuccess) + { + list = PK11_ListCerts(PK11CertListAll, NULL); + for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + node = CERT_LIST_NEXT(node)) { + retval << QByteArray((const char*)node->cert->derCert.data, + (int)node->cert->derCert.len); + } + CERT_DestroyCertList(list); + NSS_Shutdown(); + } + else + { + qDebug("Could not open nss certificate store!\n"); + } + return retval; +} + +void NSSTest::setupTestDir(QTemporaryDir *nssDir) +{ + /* Copy the empty nss db in the temporary dir */ + QFile::copy(":/nss/cert8.db", nssDir->path() + "/" +"cert8.db"); + QFile::copy(":/nss/key3.db", nssDir->path() + "/" +"key3.db"); + QFile::copy(":/nss/secmod.db", nssDir->path() + "/" +"secmod.db"); + + QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"cert8.db", + QFileDevice::ReadOwner | QFileDevice::WriteOwner)); + QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"key3.db", + QFileDevice::ReadOwner | QFileDevice::WriteOwner)); + QVERIFY(QFile::setPermissions(nssDir->path() + "/" +"secmod.db", + QFileDevice::ReadOwner | QFileDevice::WriteOwner)); +} + +void NSSTest::initTestCase() { + + /* Set up a temporary list */ + QFile res(":/list-valid-signed.txt"); + res.open(QIODevice::ReadOnly); + validListFile.open(); + validListFile.write(res.readAll()); + validListFile.close(); + + setupTestDir(&ffNSSDir); + setupTestDir(&tbNSSDir); + + validList = CertificateList(validListFile.fileName().toLocal8Bit().data()); + + /* Create the profiles.ini `s set environment variables*/ +#ifndef WIN32 + QVERIFY(!setenv ("HOME", fakeHome.path().toLocal8Bit().constData(), 1)); + fakeFirefoxDir = QDir(fakeHome.path() + "/.mozilla/firefox"); + fakeThunderbirdDir = QDir(fakeHome.path() + "/.thunderbird"); +#else + QVERIFY(!setenv ("APPDATA", fakeHome.path().toLocal8Bit().constData(), 1)); + fakeFirefoxDir = QDir(fakeHome.path() + "/Mozilla/firefox"); + fakeThunderbirdDir = QDir(fakeHome.path() + "/Thunderbird"); +#endif + QVERIFY(fakeFirefoxDir.mkpath(fakeFirefoxDir.path())); + QVERIFY(fakeThunderbirdDir.mkpath(fakeThunderbirdDir.path())); + + QFile mozProfile(fakeFirefoxDir.absoluteFilePath("profiles.ini")); + QFile tbProfile(fakeThunderbirdDir.absoluteFilePath("profiles.ini")); + + /* Write profiles */ + QVERIFY(mozProfile.open(QIODevice::WriteOnly)); + QTextStream ffStream(&mozProfile); + ffStream << endl << "[General]"<< + "StartWithLastProfile=1" << endl << + "[Profile0]" << endl << + "Name=default" << endl << + "IsRelative=1" << endl << + "Path=" << fakeFirefoxDir.relativeFilePath(ffNSSDir.path()) << endl; + ffStream.flush(); + mozProfile.close(); + + QVERIFY(tbProfile.open(QIODevice::WriteOnly)); + QTextStream tbStream(&tbProfile); + tbStream << endl << "[General]"<< + "StartWithLastProfile=1" << endl << + "[Profile102]" << endl << + "Name=default" << endl << + "IsRelative=0" << endl << + "Path=" << tbNSSDir.path() << endl; + tbStream.flush(); + tbProfile.close(); +} + +void NSSTest::testInstRemove() { + char ** to_install = NULL, + ** to_remove = NULL; + + QList instList; + + /* Install all certificates */ + foreach (const Certificate &cert, validList.getCertificates()) { + if (!cert.isInstallCert()) + continue; + instList << cert; + strv_append (&to_install, cert.base64Line().toLatin1().constData() + 2, + cert.base64Line().size() - 2); + } + QVERIFY((size_t) instList.size() == strv_length(to_install)); + + QVERIFY(write_stores_nss(to_install, to_remove) == 0); + + { + /* Verify that everything is installed */ + QList installedCertsFF = get_nss_certs(&ffNSSDir); + QList installedCertsTB = get_nss_certs(&tbNSSDir); + + QVERIFY(installedCertsFF.size() == instList.size()); + QVERIFY(installedCertsFF == installedCertsTB); + + for (int i = 0; to_install[i]; i++) { + QByteArray bai = QByteArray::fromBase64(to_install[i]); + QVERIFY(installedCertsFF.contains(bai)); + } + } + + { + /* Remove one certificate */ + QVERIFY(instList.size() > 2); + strv_append (&to_remove, to_install[1], qstrlen(to_install[1])); + + QVERIFY(write_stores_nss(NULL, to_remove) == 0); + + QList installedCertsFF = get_nss_certs(&ffNSSDir); + QList installedCertsTB = get_nss_certs(&tbNSSDir); + + QVERIFY(installedCertsFF == installedCertsTB); + + QByteArray bai = QByteArray::fromBase64(to_install[1]); + QVERIFY(!installedCertsTB.contains(bai)); + + QVERIFY((size_t)installedCertsTB.size() == strv_length(to_install) - 1); + + for (int i = 0; to_install[i]; i++) { + if (i == 1) { + continue; + } + QByteArray bai = QByteArray::fromBase64(to_install[i]); + QVERIFY(installedCertsTB.contains(bai)); + } + } + + { + /* Readd all certificates check for duplication*/ + QVERIFY(write_stores_nss(to_install, NULL) == 0); + + QList installedCertsFF = get_nss_certs(&ffNSSDir); + QList installedCertsTB = get_nss_certs(&tbNSSDir); + + QVERIFY(installedCertsFF == installedCertsTB); + + QVERIFY((size_t)installedCertsTB.size() == strv_length(to_install)); + + for (int i = 0; to_install[i]; i++) { + QByteArray bai = QByteArray::fromBase64(to_install[i]); + QVERIFY(installedCertsTB.contains(bai)); + } + } + + { + /* Remove all certificates */ + QVERIFY(write_stores_nss(NULL, to_install) == 0); + + QList installedCertsFF = get_nss_certs(&ffNSSDir); + QList installedCertsTB = get_nss_certs(&tbNSSDir); + + QVERIFY(installedCertsFF == installedCertsTB); + + QVERIFY(installedCertsTB.size() == 0); + } +} + +QTEST_GUILESS_MAIN (NSSTest); diff -r fa37384b86b6 -r f758460ca437 ui/tests/nsstest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/tests/nsstest.h Fri Apr 04 09:54:19 2014 +0200 @@ -0,0 +1,32 @@ +#ifndef NSSTEST_H +#define NSSTEST_H + +#include +#include +#include +#include +#include + +#include "certificatelist.h" + +class NSSTest: public QObject +{ + Q_OBJECT + + QTemporaryDir fakeHome; + QDir fakeFirefoxDir; + QDir fakeThunderbirdDir; + QTemporaryDir ffNSSDir; + QTemporaryDir tbNSSDir; + CertificateList validList; + QTemporaryFile validListFile; +private: + QList get_nss_certs(QTemporaryDir *nssDir); + void setupTestDir(QTemporaryDir *nssDir); + +private Q_SLOTS: + void initTestCase(); + void testInstRemove(); +}; + +#endif // NSSTEST_H