# HG changeset patch # User Emanuel Schuetze # Date 1411382406 -7200 # Node ID 0651afddb4ec80e1c2982fc35576213718854809 # Parent f8ad8fc464db4be7c1aa1be26a7e1263557ba770# Parent 59b3c22a404c5a4faafff8fb073b609a586d84e7 Merged. diff -r f8ad8fc464db -r 0651afddb4ec cinst/CMakeLists.txt --- a/cinst/CMakeLists.txt Mon Sep 22 12:38:06 2014 +0200 +++ b/cinst/CMakeLists.txt Mon Sep 22 12:40:06 2014 +0200 @@ -11,92 +11,92 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common) -set(CINST_SOURCES +set(trustbridge-certificate-installer_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/windowsstore.c ${CMAKE_CURRENT_SOURCE_DIR}/nssstore_linux.c ${CMAKE_CURRENT_SOURCE_DIR}/nssstore_win.c - ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/certificate-installer.c ) -add_executable(cinst ${CINST_SOURCES}) +add_executable(trustbridge-certificate-installer ${trustbridge-certificate-installer_SOURCES}) if (WIN32) set(WIN_EXTRA_LIBS -lcrypt32 -luserenv -lshell32) endif(WIN32) -target_link_libraries(cinst +target_link_libraries(trustbridge-certificate-installer trustbridge_common ${PROFILING_LIBS} ${POLARSSL_LIBRARIES} ${WIN_EXTRA_LIBS}) -install(TARGETS cinst DESTINATION bin) +install(TARGETS trustbridge-certificate-installer DESTINATION bin) if (WIN32) add_custom_command( - TARGET cinst + TARGET trustbridge-certificate-installer POST_BUILD - COMMAND ${CMAKE_STRIP} cinst.exe + COMMAND ${CMAKE_STRIP} trustbridge-certificate-installer.exe ) if (NOT RELEASE_BUILD) add_custom_command( - TARGET cinst + TARGET trustbridge-certificate-installer POST_BUILD COMMAND ${OSSLSIGNCODE_EXECUTABLE} sign -certs ${CMAKE_SOURCE_DIR}/ui/tests/data/codesign/codesigning.pem -key ${CMAKE_SOURCE_DIR}/ui/tests/data/codesign/codesigning.key - -h sha256 -in ${CMAKE_CURRENT_BINARY_DIR}/cinst.exe - -out ${CMAKE_CURRENT_BINARY_DIR}/cinst-signed.exe && - mv ${CMAKE_CURRENT_BINARY_DIR}/cinst-signed.exe ${CMAKE_CURRENT_BINARY_DIR}/cinst.exe + -h sha256 -in ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-certificate-installer.exe + -out ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-certificate-installer-signed.exe && + mv ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-certificate-installer-signed.exe ${CMAKE_CURRENT_BINARY_DIR}/cinst.exe ) endif() else() add_custom_command( - TARGET cinst + TARGET trustbridge-certificate-installer POST_BUILD - COMMAND strip cinst + COMMAND strip trustbridge-certificate-installer ) endif() # ---------------------------------------------------------------------- -# Mozilla nss store specific certificate installer: -set(MOZILLA_SOURCES +# trustbridge-nss-installer nss store specific certificate installer: +set(trustbridge-nss-installer_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/nss-secitemlist.c - ${CMAKE_CURRENT_SOURCE_DIR}/mozilla.c + ${CMAKE_CURRENT_SOURCE_DIR}/nss-installer.c ) if(WIN32 OR NSS_FOUND) include_directories(${NSS_INCLUDE_DIRS}) - add_executable(mozilla ${MOZILLA_SOURCES}) - target_link_libraries(mozilla + add_executable(trustbridge-nss-installer ${trustbridge-nss-installer_SOURCES}) + target_link_libraries(trustbridge-nss-installer trustbridge_common ${POLARSSL_LIBRARIES} ${NSS_LIBRARIES} ${PROFILING_LIBS}) - set_target_properties(mozilla PROPERTIES COMPILE_FLAGS "-std=c99") - install(TARGETS mozilla DESTINATION bin) + set_target_properties(trustbridge-nss-installer PROPERTIES COMPILE_FLAGS "-std=c99") + install(TARGETS trustbridge-nss-installer DESTINATION bin) if (WIN32) add_custom_command( - TARGET mozilla + TARGET trustbridge-nss-installer POST_BUILD - COMMAND ${CMAKE_STRIP} mozilla.exe + COMMAND ${CMAKE_STRIP} trustbridge-nss-installer.exe ) if (NOT RELEASE_BUILD) add_custom_command( - TARGET mozilla + TARGET trustbridge-nss-installer POST_BUILD COMMAND ${OSSLSIGNCODE_EXECUTABLE} sign -certs ${CMAKE_SOURCE_DIR}/ui/tests/data/codesign/codesigning.pem -key ${CMAKE_SOURCE_DIR}/ui/tests/data/codesign/codesigning.key - -h sha256 -in ${CMAKE_CURRENT_BINARY_DIR}/mozilla.exe - -out ${CMAKE_CURRENT_BINARY_DIR}/mozilla-signed.exe && - mv ${CMAKE_CURRENT_BINARY_DIR}/mozilla-signed.exe ${CMAKE_CURRENT_BINARY_DIR}/mozilla.exe + -h sha256 -in ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer.exe + -out ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer-signed.exe && + mv ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer-signed.exe ${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer.exe ) endif() else() add_custom_command( - TARGET mozilla + TARGET trustbridge-nss-installer POST_BUILD - COMMAND strip mozilla + COMMAND strip trustbridge-nss-installer ) endif() else() - message(STATUS "WARNING: Could not find nss. Mozilla cert installer will not be build!") + message(STATUS "WARNING: Could not find nss. trustbridge-nss-installer cert installer will not be build!") endif() diff -r f8ad8fc464db -r 0651afddb4ec cinst/README --- a/cinst/README Mon Sep 22 12:38:06 2014 +0200 +++ b/cinst/README Mon Sep 22 12:40:06 2014 +0200 @@ -9,8 +9,8 @@ There is a test list in ui/tests that you can use to manually invoke the command. e.g.: -./cinst list=/ui/tests/data/list-valid-signed.txt choices=uninstall +./trustbridge-certificate-installer list=/ui/tests/data/list-valid-signed.txt choices=uninstall A choices file lies next to it. -./cinst list=/ui/tests/data/list-valid-signed.txt \ +./trustbrdige-certificate-installer list=/ui/tests/data/list-valid-signed.txt \ choices=/ui/tests/data/choices-example.txt diff -r f8ad8fc464db -r 0651afddb4ec cinst/certificate-installer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/certificate-installer.c Mon Sep 22 12:40:06 2014 +0200 @@ -0,0 +1,357 @@ +/* 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. + */ +/** + * @file certificate-installer.c + * @brief Main entry point for the certificate install process. + * + * The certificate installer process may or may not be run with elevated + * privileges. When run with elevated privileges this + * process will modify system wide certificate stores. + * Otherwise only the users certificate stores are modified. + * + * The first parameter to this process should be list=\ + * of the certificate list to work on. The second parameter should + * be choices=\|uninstall + * + * choices_file_name should be the absolute path to an + * choices file formatted as: + * + * I:\
+ * R:\ + * + * Line breaks can be system dependent in the Choices file. + * + * It will only execute the choices if the + * I and R choices 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 + * to remove all certificates (Even those marked with I) that + * are part of the list. + * + * For more verbose debug output add --debug to the call. + * + **/ +#include +#include +#include +#include +#include + +#include "strhelp.h" +#include "listutil.h" +#include "logging.h" +#include "errorcodes.h" +#include "windowsstore.h" +#include "nssstore.h" +#include "portpath.h" + +/* The certificate list + choices may only be so long as + * twice the accepted certificatelist size */ +#define MAX_INPUT_SIZE MAX_LINE_LENGTH * MAX_LINES * 2 + +/* @brief Read stdin into data structures. + * + * Reads choices from an input file into the to_install + * and to_remove buffers. + * + * Lines starting with I: are treated as install choices. + * Lines starting with R: are treated as remove choices. + * 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. + * + * @param[in] file_name absolute path to the choices file. + * @param[out] to_install strv of installation choices or NULL + * @param[out] to_remove strv of remove choices or NULL + * + * @returns: 0 on success. An error code otherwise. + */ +static int +read_choices_file (char *file_name, char ***to_install, + char ***to_remove) +{ + int lines_read = 0; + char buf[MAX_LINE_LENGTH + 2]; + FILE *f = NULL; + long file_size; + + if (*to_install || *to_remove) + { + ERRORPRINTF ("Error invalid parameters.\n"); + return -1; + } + + f = port_fopen_rb(file_name, false); + if (f == NULL) + { + ERRORPRINTF ("Failed to open file: %s\n", file_name); + return ERR_NO_INSTRUCTIONS; + } + + fseek (f, 0, SEEK_END); + file_size = ftell (f); + if (file_size <= 0) + { + fclose (f); + ERRORPRINTF ("File size error: %s\n", file_name); + return ERR_NO_INSTRUCTIONS; + } + + fseek (f, 0, SEEK_SET); + + if (file_size + 1 == 0) + { + fclose (f); + ERRORPRINTF ("File seek error: %s\n", file_name); + 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) + { + ERRORPRINTF ("Line too short.\n"); + fclose (f); + return ERR_INVALID_INPUT; + } + if (lines_read++ > MAX_LINES) + { + ERRORPRINTF ("Too many lines\n"); + fclose (f); + return ERR_TOO_MUCH_INPUT; + } + if (*buf == 'I') + { + char *trimmed = buf+2; + /* Remove leading I: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_install, trimmed, strlen(trimmed)); + continue; + } + if (*buf == 'R') + { + char *trimmed = buf+2; + /* Remove leading R: and trailing whitespace */ + str_trim(&trimmed); + strv_append (to_remove, trimmed, strlen(trimmed)); + continue; + } + } + + fclose (f); + return 0; +} + +/** @brief Check that the insturctions match to the list + * + * Only certificates part of the certificate_list are allowed + * for installation. + * + * @param[in] all_certs strv of all valid certificates in a list + * @param[in] to_validate strv of choices + * + * @returns 0 on success, an error otherwise + */ +int +validate_choices (char **all_certs, char **to_validate) +{ + int i = 0, j = 0; + + if (!all_certs || strv_length (all_certs) < 1) + { + /* Invalid parameters */ + return -1; + } + + if (to_validate == NULL) + { + /* Nothing is valid */ + return 0; + } + + for (i = 0; to_validate[i]; i++) + { + bool found = false; + for (j = 0; all_certs[j]; j++) + { + if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH) == + 0) + { + found = true; + break; + } + } + if (!found) + { + DEBUGPRINTF ("Failed to find certificate; \n%s\n", to_validate[i]); + return ERR_INVALID_INSTRUCTIONS; + } + } + + return 0; +} + +#ifdef DO_RELEASE_BUILD +bool g_debug = false; +#else +bool g_debug = true; +#endif + +int +main (int argc, char **argv) +{ + char **to_install = NULL, + **to_remove = NULL, + **all_valid_certs = NULL; + int ret = -1; + + char *certificate_list = NULL, + *certificate_file_name = NULL, + *choices_file_name = NULL; + size_t list_len = 0; + list_status_t list_status; + bool do_uninstall = false; + + /* Some very static argument parsing. list= and choices= 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 && argc != 4) || strncmp(argv[1], "list=", 5) != 0 || + strncmp(argv[2], "choices=", 8) != 0) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list= \n" + " choices=|uninstall\n" + "Optional: --debug\n"); + return ERR_INVALID_PARAMS; + } + + certificate_file_name = strchr(argv[1], '=') + 1; + choices_file_name = strchr(argv[2], '=') + 1; + + if (argc == 4 && strncmp(argv[3], "--debug", 7) == 0) + { + g_debug = true; + } + + if (!certificate_file_name || !choices_file_name) + { + ERRORPRINTF ("Invalid arguments.\n" + "Expected arguments: list= \n" + " choices=|uninstall\n"); + return ERR_INVALID_PARAMS; + } + + if (strncmp(choices_file_name, "uninstall", 9) == 0) + { + do_uninstall = true; + choices_file_name = NULL; + } + + list_status = read_and_verify_list (certificate_file_name, &certificate_list, + &list_len); + + if (list_status != Valid) + { + if (list_status == InvalidSignature) + { + ERRORPRINTF ("Failed to verify signature.\n"); + return ERR_INVALID_SIGNATURE; + } + + ERRORPRINTF ("Failed to read certificate list.\n"); + return ERR_INVALID_INPUT_NO_LIST; + } + + all_valid_certs = get_certs_from_list (certificate_list, list_len); + free (certificate_list); + + 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); + strv_free (all_valid_certs); + return ret; + } + + ret = read_choices_file (choices_file_name, &to_install, + &to_remove); + + if (ret) + { + ERRORPRINTF ("Failed to read choices file\n"); + return ret; + } + + if (!strv_length (to_install) && !strv_length (to_remove) ) + { + ERRORPRINTF ("Failed to read choices file\n"); + return ERR_NO_INSTRUCTIONS; + } + + /* Check that the choices are ok to execute */ + if (to_install) + { + ret = validate_choices (all_valid_certs, to_install); + if (ret) + { + ERRORPRINTF ("Failed to validate choices\n"); + return ret; + } + } + + if (to_remove) + { + ret = validate_choices (all_valid_certs, to_remove); + if (ret) + { + ERRORPRINTF ("Failed to validate removal choices\n"); + return ret; + } + } + + strv_free (all_valid_certs); + +#ifdef WIN32 + ret = write_stores_win (to_install, to_remove); + if (ret != 0) + { + ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); + } +#endif + ret = write_stores_nss (to_install, to_remove); + if (ret != 0) + { + ERRORPRINTF ("Failed to write nss stores"); + } + + /* Make valgrind happy */ + strv_free (to_install); + strv_free (to_remove); + + return 0; +} diff -r f8ad8fc464db -r 0651afddb4ec cinst/main.c --- a/cinst/main.c Mon Sep 22 12:38:06 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,357 +0,0 @@ -/* 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. - */ -/** - * @file main.c - * @brief Main entry point for the cinst process. - * - * The cinst process may or may not be run with elevated - * privileges. When run with elevated privileges this - * process will modify system wide certificate stores. - * Otherwise only the users certificate stores are modified. - * - * The first parameter to this process should be list=\ - * of the certificate list to work on. The second parameter should - * be choices=\|uninstall - * - * choices_file_name should be the absolute path to an - * choices file formatted as: - * - * I:\
- * R:\ - * - * Line breaks can be system dependent in the Choices file. - * - * It will only execute the choices if the - * I and R choices 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 - * to remove all certificates (Even those marked with I) that - * are part of the list. - * - * For more verbose debug output add --debug to the call. - * - **/ -#include -#include -#include -#include -#include - -#include "strhelp.h" -#include "listutil.h" -#include "logging.h" -#include "errorcodes.h" -#include "windowsstore.h" -#include "nssstore.h" -#include "portpath.h" - -/* The certificate list + choices may only be so long as - * twice the accepted certificatelist size */ -#define MAX_INPUT_SIZE MAX_LINE_LENGTH * MAX_LINES * 2 - -/* @brief Read stdin into data structures. - * - * Reads choices from an input file into the to_install - * and to_remove buffers. - * - * Lines starting with I: are treated as install choices. - * Lines starting with R: are treated as remove choices. - * 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. - * - * @param[in] file_name absolute path to the choices file. - * @param[out] to_install strv of installation choices or NULL - * @param[out] to_remove strv of remove choices or NULL - * - * @returns: 0 on success. An error code otherwise. - */ -static int -read_choices_file (char *file_name, char ***to_install, - char ***to_remove) -{ - int lines_read = 0; - char buf[MAX_LINE_LENGTH + 2]; - FILE *f = NULL; - long file_size; - - if (*to_install || *to_remove) - { - ERRORPRINTF ("Error invalid parameters.\n"); - return -1; - } - - f = port_fopen_rb(file_name, false); - if (f == NULL) - { - ERRORPRINTF ("Failed to open file: %s\n", file_name); - return ERR_NO_INSTRUCTIONS; - } - - fseek (f, 0, SEEK_END); - file_size = ftell (f); - if (file_size <= 0) - { - fclose (f); - ERRORPRINTF ("File size error: %s\n", file_name); - return ERR_NO_INSTRUCTIONS; - } - - fseek (f, 0, SEEK_SET); - - if (file_size + 1 == 0) - { - fclose (f); - ERRORPRINTF ("File seek error: %s\n", file_name); - 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) - { - ERRORPRINTF ("Line too short.\n"); - fclose (f); - return ERR_INVALID_INPUT; - } - if (lines_read++ > MAX_LINES) - { - ERRORPRINTF ("Too many lines\n"); - fclose (f); - return ERR_TOO_MUCH_INPUT; - } - if (*buf == 'I') - { - char *trimmed = buf+2; - /* Remove leading I: and trailing whitespace */ - str_trim(&trimmed); - strv_append (to_install, trimmed, strlen(trimmed)); - continue; - } - if (*buf == 'R') - { - char *trimmed = buf+2; - /* Remove leading R: and trailing whitespace */ - str_trim(&trimmed); - strv_append (to_remove, trimmed, strlen(trimmed)); - continue; - } - } - - fclose (f); - return 0; -} - -/** @brief Check that the insturctions match to the list - * - * Only certificates part of the certificate_list are allowed - * for installation. - * - * @param[in] all_certs strv of all valid certificates in a list - * @param[in] to_validate strv of choices - * - * @returns 0 on success, an error otherwise - */ -int -validate_choices (char **all_certs, char **to_validate) -{ - int i = 0, j = 0; - - if (!all_certs || strv_length (all_certs) < 1) - { - /* Invalid parameters */ - return -1; - } - - if (to_validate == NULL) - { - /* Nothing is valid */ - return 0; - } - - for (i = 0; to_validate[i]; i++) - { - bool found = false; - for (j = 0; all_certs[j]; j++) - { - if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH) == - 0) - { - found = true; - break; - } - } - if (!found) - { - DEBUGPRINTF ("Failed to find certificate; \n%s\n", to_validate[i]); - return ERR_INVALID_INSTRUCTIONS; - } - } - - return 0; -} - -#ifdef DO_RELEASE_BUILD -bool g_debug = false; -#else -bool g_debug = true; -#endif - -int -main (int argc, char **argv) -{ - char **to_install = NULL, - **to_remove = NULL, - **all_valid_certs = NULL; - int ret = -1; - - char *certificate_list = NULL, - *certificate_file_name = NULL, - *choices_file_name = NULL; - size_t list_len = 0; - list_status_t list_status; - bool do_uninstall = false; - - /* Some very static argument parsing. list= and choices= 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 && argc != 4) || strncmp(argv[1], "list=", 5) != 0 || - strncmp(argv[2], "choices=", 8) != 0) - { - ERRORPRINTF ("Invalid arguments.\n" - "Expected arguments: list= \n" - " choices=|uninstall\n" - "Optional: --debug\n"); - return ERR_INVALID_PARAMS; - } - - certificate_file_name = strchr(argv[1], '=') + 1; - choices_file_name = strchr(argv[2], '=') + 1; - - if (argc == 4 && strncmp(argv[3], "--debug", 7) == 0) - { - g_debug = true; - } - - if (!certificate_file_name || !choices_file_name) - { - ERRORPRINTF ("Invalid arguments.\n" - "Expected arguments: list= \n" - " choices=|uninstall\n"); - return ERR_INVALID_PARAMS; - } - - if (strncmp(choices_file_name, "uninstall", 9) == 0) - { - do_uninstall = true; - choices_file_name = NULL; - } - - list_status = read_and_verify_list (certificate_file_name, &certificate_list, - &list_len); - - if (list_status != Valid) - { - if (list_status == InvalidSignature) - { - ERRORPRINTF ("Failed to verify signature.\n"); - return ERR_INVALID_SIGNATURE; - } - - ERRORPRINTF ("Failed to read certificate list.\n"); - return ERR_INVALID_INPUT_NO_LIST; - } - - all_valid_certs = get_certs_from_list (certificate_list, list_len); - free (certificate_list); - - 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); - strv_free (all_valid_certs); - return ret; - } - - ret = read_choices_file (choices_file_name, &to_install, - &to_remove); - - if (ret) - { - ERRORPRINTF ("Failed to read choices file\n"); - return ret; - } - - if (!strv_length (to_install) && !strv_length (to_remove) ) - { - ERRORPRINTF ("Failed to read choices file\n"); - return ERR_NO_INSTRUCTIONS; - } - - /* Check that the choices are ok to execute */ - if (to_install) - { - ret = validate_choices (all_valid_certs, to_install); - if (ret) - { - ERRORPRINTF ("Failed to validate choices\n"); - return ret; - } - } - - if (to_remove) - { - ret = validate_choices (all_valid_certs, to_remove); - if (ret) - { - ERRORPRINTF ("Failed to validate removal choices\n"); - return ret; - } - } - - strv_free (all_valid_certs); - -#ifdef WIN32 - ret = write_stores_win (to_install, to_remove); - if (ret != 0) - { - ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); - } -#endif - ret = write_stores_nss (to_install, to_remove); - if (ret != 0) - { - ERRORPRINTF ("Failed to write nss stores"); - } - - /* Make valgrind happy */ - strv_free (to_install); - strv_free (to_remove); - - return 0; -} diff -r f8ad8fc464db -r 0651afddb4ec cinst/mozilla.c --- a/cinst/mozilla.c Mon Sep 22 12:38:06 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,925 +0,0 @@ -/* 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. - */ -/** - * @file - * @brief Mozilla installation process - * - * Reads from a file given on command line or stdin a list of - * instructions in the form: - * - * I:\
- * R:\ - * ... - * - * With one instruction per line. the maximum size of an input - * line is 9999 characters (including the \\r\\n) at the end of the line. - * - * Certificates marked with I: will be installed and the ones - * marked with R: will be searched and if available removed from - * the databases. - * - * This tool tries to find all NSS databases the user has - * access to and to execute the instructions on all of them. - * - * If the tool is executed with a UID of 0 or with admin privileges under - * windows it will not look into the user directories but instead try - * to write the system wide defaults. - * - * If there are other processes accessing the databases the caller - * has to ensure that those are terminated before this process is - * executed. - * - * If the same certificate is marked to be installed and to be removed - * in one call the behavior is undefined. This should be avoided and - * may lead to errors. - * - * Returns 0 on success (Even when no stores where found) an error value - * as defined in errorcodes.h otherwise. - * - * Success messages are written to stdout. Errors to stderr. For logging - * purposes each installation / removal of a certificate will be reported - * with the profile name that it modified. - * - * To get more verbose output add the --debug parameter - * as the last parameter on the command line. - * - */ - -/** - * @brief Needs to be defined to get strnlen() - */ -#define _POSIX_C_SOURCE 200809L - -/* REMOVEME: */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUGPREFIX "MOZ-" -#include "logging.h" - -#include "certhelp.h" -#include "errorcodes.h" -#include "portpath.h" -#include "strhelp.h" -#include "nss-secitemlist.h" -#include "util.h" - -#ifndef _WIN32 -#define CONFDIRS ".mozilla", ".thunderbird" -/* Default installation directory of ubuntu 14.4 is respected */ -#define MOZILLA_DEFAULTS "/usr/lib/thunderbird/defaults", "/usr/lib/firefox/browser/defaults" -#define MOZILLA_DBNAMES "cert8.db", "key3.db", "secmod.db" -#define NSSSHARED ".pki/nssdb" -#define NSSSHARED_GLOBAL "/etc/skel/.pki/nssdb" -#define TARGET_LINUX 1 -#define DIRSEP "/" -#else -#define MOZILLA_DEFAULTS "Mozilla Firefox\\browser\\defaults", "Mozilla Thunderbird\\defaults" -#define MOZILLA_DBNAMES NULL -#define CONFDIRS "Mozilla", "Thunderbird" -#define NSSSHARED "" -#define TARGET_LINUX NULL -#define DIRSEP "\\" -#endif - -/** - * @brief Length of string buffers used - * - * The maximal length of input is defined as 9999 (+ terminating \0). - * We use it for other other input puffers besides the IPC input, too. - * (One size fits all). - */ -#define LINEBUFLEN 10000 - -#ifdef _WIN32 -#define STRTOK_R strtok_s -#else -#define STRTOK_R strtok_r -#endif - -/** - * @brief Global Return Code - * - * This will be retuned by the programm and might be set to an - * error code on fatal errors and to and warning code on non-fatal - * errors. In case of mor than one warning the warning codes will be - * ORed together. - */ -int exit_code = 0; - -/** - * @brief Return configuration base directory. - * @returns A pointer to a string containing the path to the base - * directory holding the configuration directories for e.g. mozilla - * and thunderbird. - */ -static char * -get_conf_basedir() -{ - char *cdir, *envvar; - - if (TARGET_LINUX) - envvar = "HOME" ; - else - envvar = "APPDATA"; - - if ((cdir = getenv(envvar)) != NULL) - return cdir; - else - { - ERRORPRINTF("FATAL! No %s in environment.\n", envvar); - exit(ERR_MOZ_HOMELESS); - } -} - -/** - * @brief Get a list of all mozilla profile directories - * - * Parse the profiles.ini and extract all profile paths from that. - * The expected data is in the form: - * - * [Profile99]
- * IsRelative=1
- * Path=Example/foo.bar - * - * or
- * [Profile0]
- * IsRelative=0
- * Path=c:\\foo\\bar\\baz - * - * Mozilla also accepts the ini file on Windows even if it is UTF-16 - * encoded but never writes UTF-16 on its own. So currently we ignore - * this special case. - * - * @param[in] inifile_name path of the profile.ini to read. - * @return NULL terminated array of strings containing containing the - * absolute path of the profile directories. The array needs to - * be freed by the caller. - */ -static char ** -get_profile_dirs (char *inifile_name) -{ - char **dirs = NULL; - char *inifile_dirname; - FILE *inifile; - char line[LINEBUFLEN]; - char *key; - char *value; - char *path = NULL; - char *fqpath; - bool inprofile = false; - bool relative_path = false; - char *saveptr; - - if ((inifile = fopen(inifile_name, "r")) != NULL) - { - DEBUGPRINTF("Searching for profile paths in: '%s'\n", inifile_name); - - inifile_dirname = port_dirname(inifile_name); - while (fgets(line, LINEBUFLEN, inifile) != NULL) - { - /* Determine if we are in an profile section */ - if (str_starts_with(line, "[Profile")) - { - relative_path = false; - inprofile = true; - } - else if (line[0] == '[') - inprofile = false; - - /* If we are in a profile parse path related stuff */ - if (inprofile) - { - saveptr = NULL; - key = STRTOK_R(line, "=", &saveptr); - value = STRTOK_R(NULL, "=", &saveptr); - str_trim(&value); - if (str_equal(key, "Path")) - { - if (relative_path) - xasprintf(&path, "%s/%s", inifile_dirname, value); - else - xasprintf(&path, "%s", value); - if ((fqpath = port_realpath(path)) != NULL) - { - DEBUGPRINTF("Found profile path: '%s'\n", fqpath); - strv_append(&dirs, fqpath, strlen(fqpath)); - free (fqpath); - } - else - { - DEBUGPRINTF("WARN! Non existent profile path: '%s'\n", path); - exit_code |= WARN_MOZ_PROFILE_DOES_NOT_EXIST; - } - free(path); - } - else if (str_equal(key, "IsRelative") && - str_starts_with(value, "1")) - relative_path = true; - } - } - fclose(inifile); - } - else - { - DEBUGPRINTF("WARN! Could not open ini file: '%s'\n", inifile_name); - exit_code |= WARN_MOZ_FAILED_TO_OPEN_INI; - } - return dirs; -} - -/** - * @brief Search for mozilla profiles.ini files - * - * Use well known paths and heuristics to find the current users - * profiles.ini files on GNU/Linux and Windows systems. - * - * @return NULL terminated array of strings containing the absolute - * path of the profiles.ini files. The array needs to be freed by the - * caller. - */ -static char ** -get_profile_inis () -{ - char **inis = NULL; - char *mozpath, *fqpath, *subpath, *ppath; - DIR *mozdir; - struct dirent *mozdirent; - char *confbase = get_conf_basedir(); - const char *confdirs[] = { CONFDIRS, NULL }; - - for (int i=0; confdirs[i] != NULL; i++) - { - xasprintf(&mozpath,"%s/%s", confbase, confdirs[i]); - if ((mozdir = opendir(mozpath)) != NULL) - { - while ((mozdirent = readdir(mozdir)) != NULL) - { - xasprintf(&subpath, "%s/%s/%s", - confbase, - confdirs[i], - mozdirent->d_name); - if (port_isdir(subpath) - && (strcmp(mozdirent->d_name, "..") != 0)) - { - xasprintf(&ppath, "%s/%s/%s/%s", - confbase, - confdirs[i], - mozdirent->d_name, - "profiles.ini"); - DEBUGPRINTF("checking for %s...\n", ppath); - if ((fqpath = port_realpath(ppath)) != NULL) - { - strv_append(&inis, fqpath, strlen(fqpath)); - DEBUGPRINTF("Found mozilla ini file: '%s'\n", fqpath); - free(fqpath); - } - free(ppath); - } - free(subpath); - } - closedir(mozdir); - } - else - { - DEBUGPRINTF("Could not open %s/%s\n", confbase, confdirs[i]); - } - free(mozpath); - } - if (inis == NULL) - { - DEBUGPRINTF("No ini files found - will do nothing!\n"); - } - return inis; -} - - -/** @brief make the default nss databases readable. - * - * This uses the static paths definied in this code to ensure - * that only the defaults are touched. - * - */ -#ifndef WIN32 -static void -make_defaults_readable() -{ - const char *confdirs[] = { MOZILLA_DEFAULTS, NULL }; - const char *filenames[] = { MOZILLA_DBNAMES, NULL }; - - mode_t access_mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - - for (int i=0; confdirs[i] != NULL; i++) - { - for (int j=0; filenames[j] != NULL; j++) - { - char *realpath = NULL, - *path = NULL; - xasprintf (&path, "%s/profile/%s", confdirs[i], filenames[j]); - realpath = port_realpath(path); - xfree(path); - if (!realpath) - { - syslog_error_printf("Failed to find %s \n", realpath); - continue; - } - if (chmod(realpath, access_mask)) - { - syslog_error_printf("Failed to set access_mask on file.\n"); - } - xfree (realpath); - } - } -} -#endif - -/** - * @brief Collect the default profile directories for mozilla software - * - * If the default directory is found but not the profiles subdirectory - * this will create the profiles subdirectory. - * - * @return NULL terminated array of strings containing the absolute path - * to the default profile directories. Needs to be freed by the caller. - */ -static char** -get_default_profile_dirs() -{ - char **retval = NULL; - - const char *confdirs[] = { MOZILLA_DEFAULTS, NULL }; - -#ifdef _WIN32 - char *program_files = get_program_files_folder(); - if (!program_files) - { - ERRORPRINTF ("Failed to look up program files folder.\n"); - return NULL; - } -#endif - - for (int i=0; confdirs[i] != NULL; i++) - { - char *realpath = NULL, - *profile_dir = NULL; -#ifndef _WIN32 - realpath = port_realpath(confdirs[i]); -#else - /* As on linux we only respect the default installation directory - mozilla firefox and thunderbird change their registry key with - each version as the key includes the version number. It would - be error prone to search the system for every instance. So we - only check the default installation directories. */ - xasprintf(&realpath, "%s" DIRSEP "%s", program_files, confdirs[i]); -#endif - if (realpath == NULL) - { - DEBUGPRINTF ("Did not find directory: '%s'\n", confdirs[i]); - continue; - } - xasprintf(&profile_dir, "%s" DIRSEP "profile", realpath); - xfree(realpath); - if (port_isdir(profile_dir)) - { - DEBUGPRINTF("Found default directory: '%s'\n", profile_dir); - /* All is well */ - strv_append (&retval, profile_dir, strlen(profile_dir)); - xfree(profile_dir); - profile_dir = NULL; - continue; - } - else - { - /* Create the directory */ - if (port_fileexits(profile_dir)) - { - DEBUGPRINTF ("Path: '%s' is not a directory but it exists. Skipping.\n", - profile_dir); - xfree(profile_dir); - profile_dir = NULL; - continue; - } - else - { - /* Lets create it */ - if (!port_mkdir_p(profile_dir, true)) - { - ERRORPRINTF ("Failed to create directory: '%s'\n", profile_dir); - xfree(profile_dir); - profile_dir = NULL; - continue; - } - strv_append (&retval, profile_dir, strlen(profile_dir)); - xfree(profile_dir); - profile_dir = NULL; - } - } - } -#ifdef WIN32 - xfree (program_files); -#endif - return retval; -} - -/** - * @brief Collect all mozilla profile directories of current user. - * @return NULL terminated array of strings containing the absolute - * path of the profile directories. The array needs to be freed by the - * caller. - */ -static char** -get_all_nssdb_dirs() -{ - char **mozinis, **pdirs; - char **alldirs = NULL; - - if (is_elevated()) - { -#ifndef _WIN32 - /* NSS Shared db does not exist under windows. */ - if (!port_mkdir_p(NSSSHARED_GLOBAL, false)) - { - ERRORPRINTF("Failed to create nssshared skeleton directory. \n"); - } - else - { - strv_append(&alldirs, "sql:" NSSSHARED_GLOBAL, strlen("sql:" NSSSHARED_GLOBAL)); - } -#endif - pdirs = get_default_profile_dirs(); - if (pdirs != NULL) - { - for (int i=0; pdirs[i] != NULL; i++) - { - strv_append(&alldirs, pdirs[i], strlen(pdirs[i])); - } - strv_free(pdirs); - } - return alldirs; - } - /* Search Mozilla/Firefox/Thunderbird profiles */ - if ((mozinis = get_profile_inis()) != NULL) - { - for (int i=0; mozinis[i] != NULL; i++) - { - pdirs = - get_profile_dirs(mozinis[i]); - if (pdirs != NULL) - { - for (int i=0; pdirs[i] != NULL; i++) - { - strv_append(&alldirs, pdirs[i], strlen(pdirs[i])); - } - strv_free(pdirs); - } - } - strv_free(mozinis); - } - /* Search for NSS shared DB (used by Chrome/Chromium on GNU/Linux) */ - if (TARGET_LINUX) - { - char *path, *fqpath, *sqlpath; - xasprintf(&path, "%s/%s", get_conf_basedir(), NSSSHARED); - if ((fqpath = port_realpath(path)) != NULL) - { - xasprintf(&sqlpath, "sql:%s", fqpath); - strv_append(&alldirs, sqlpath, strlen(sqlpath)); - free(sqlpath); - free(fqpath); - } - free(path); - } - return alldirs; -} - -#ifdef DEBUGOUTPUT -/** - * @brief list certificates from nss certificate store - * @param[in] confdir the directory with the certificate store - */ -static void -DEBUG_nss_list_certs (char *confdir) -{ - CERTCertList *list; - CERTCertListNode *node; - char *name; - - if (NSS_Initialize(confdir, "", "", "secmod.db", NSS_INIT_READONLY) - == SECSuccess) - { - DEBUGPRINTF("Listing certs in \"%s\"\n", confdir); - list = PK11_ListCerts(PK11CertListAll, NULL); - for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); - node = CERT_LIST_NEXT(node)) - { - name = node->appData; - - DEBUGPRINTF("Found certificate \"%s\"\n", name); - } - /* According to valgrind this leaks memory in the list. - We could not find API documentation to better free this - so we accept the leakage here in case of debug. */ - CERT_DestroyCertList(list); - NSS_Shutdown(); - } - else - { - DEBUGPRINTF("Could not open nss certificate store in %s!\n", confdir); - } -} -#endif - -/** - * @brief Create a string with the name for cert in SECItem. - * - * Should be freed by caller. - * @param[in] secitemp ponts to an SECItem holding the DER certificate. - * @returns a string of the from "CN of Subject - O of Subject" - */ -static char * -nss_cert_name(SECItem *secitemp) -{ - char *cn_str, *o_str, *name; - size_t name_len; - cn_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_CN); - o_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_O); - if (!cn_str || !o_str) - { - ERRORPRINTF("FATAL: Could not parse certificate!"); - exit(ERR_INVALID_CERT); - } - name_len = strlen(cn_str) + strlen(o_str) + 4; - name = (char *)xmalloc(name_len); - snprintf(name, name_len, "%s - %s", cn_str, o_str); - free(cn_str); - free(o_str); - return name; -} - -/** - * @brief Convert a base64 encoded DER certificate to SECItem - * @param[in] b64 pointer to the base64 encoded certificate - * @param[in] b64len length of the base64 encoded certificate - * @param[out] secitem pointer to the SECItem in which to store the - * raw DER certifiacte. - * @returns true on success and false on failure - */ -static bool -base64_to_secitem(char *b64, size_t b64len, SECItem *secitem) -{ - unsigned char *dercert = NULL; - size_t dercertlen; - - if ((str_base64_decode((char **)(&dercert), &dercertlen, - b64, b64len) == 0) && - (dercertlen > 0)) - { - secitem->data = dercert; - secitem->len = (unsigned int) dercertlen; - return true; - } - else - { - DEBUGPRINTF("Base64 decode failed for: %s\n", b64); - } - return false; -} - -/** - * @brief Store DER certificate in mozilla store. - * @param[in] pdir the mozilla profile directory with the certificate - * store to manipulate. - * @param[in] dercert pointer to a SECItem holding the DER certificate - * to install - * @returns true on success and false on failure - */ -static bool -import_cert(char *pdir, SECItem *dercert) -{ - PK11SlotInfo *pk11slot = NULL; - CERTCertTrust *trust = NULL; - CERTCertificate *cert = NULL; - bool success = false; - char *cert_name = nss_cert_name(dercert); - - DEBUGPRINTF("INSTALLING cert: '%s' to: %s\n", cert_name, pdir); - pk11slot = PK11_GetInternalKeySlot(); - cert = CERT_DecodeCertFromPackage((char *)dercert->data, - (int)dercert->len); - trust = (CERTCertTrust *)xmalloc(sizeof(CERTCertTrust)); - CERT_DecodeTrustString(trust, "C,C,C"); - if (PK11_ImportCert(pk11slot, cert, CK_INVALID_HANDLE, - cert_name, PR_FALSE) == SECSuccess) - { - if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess) - { - log_certificate_der (pdir, dercert->data, dercert->len, true); - success = true; - } - } - /* This could have happened on either the import cert or - the cert change trust. If Import Cert fails with that - error the certificate has in fact been added but with - random trist bits. See NSS Bug 595861. - Reference code can be found in gnome evolution under - smime/lib/e-cert-db.c */ - if(PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) - { - if (PK11_NeedUserInit (pk11slot)) - { - PK11_InitPin (pk11slot, "", ""); - } - if (PK11_Authenticate (pk11slot, PR_TRUE, NULL) != SECSuccess) - { - DEBUGPRINTF("Failed to authenticate.\n"); - } - else if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess) - { - log_certificate_der (pdir, dercert->data, dercert->len, true); - success = true; - } - } - - if (!success) - { - DEBUGPRINTF("Failed to install certificate '%s' to '%s'!\n", cert_name, pdir); - ERRORPRINTF("Error installing certificate err: %i\n", PORT_GetError()); - } - CERT_DestroyCertificate (cert); - free(trust); - PK11_FreeSlot(pk11slot); - - free(cert_name); - return success; -} - -/** - * @brief Remove DER certificate from mozilla store. - * @param[in] pdir the mozilla profile directory with the certificate - * store to manipulate. - * @param[in] dercert pointer to a SECItem holding the DER certificate - * to remove - * @returns true on success and false on failure - */ -static bool -remove_cert(char *pdir, SECItem *dercert) -{ - PK11SlotInfo *pk11slot = NULL; - bool success = false; - char *cert_name = nss_cert_name(dercert); - CERTCertificate *cert = NULL; - - DEBUGPRINTF("REMOVING cert: '%s' from: %s\n", cert_name, pdir); - if (NSS_Initialize(pdir, "", "", "secmod.db", 0) == SECSuccess) - { - pk11slot = PK11_GetInternalKeySlot(); - cert = PK11_FindCertFromDERCertItem(pk11slot, - dercert, NULL); - if (cert != NULL) - { - if (SEC_DeletePermCertificate(cert) == SECSuccess) - { - success = true; - log_certificate_der (pdir, dercert->data, dercert->len, false); - } - else - { - DEBUGPRINTF("Failed to remove certificate '%s' from '%s'!\n", cert_name, pdir); - } - CERT_DestroyCertificate(cert); - } - else - { - DEBUGPRINTF("Could not find Certificate '%s' in store '%s'.\n", cert_name, pdir); - } - PK11_FreeSlot(pk11slot); - NSS_Shutdown(); - } - else - { - DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdir); - } - free(cert_name); - return success; -} - -/** - * @brief Apply a function to a list of certificates and profiles - * - * The function must have the signature: - * - * bool function(char *pdir, SECItem der_cert) - * - * where pdir is the path of an profile and der_cert is an raw DER - * formatted certificate. The function must return true on success - * and false on failure. - * - * This function is intended for use with the import_cert and - * remove_cert functions. - * - * @param[in] fn the function to apply - * @param[inout] certs a secitem list holding the certificates - * the list will be change (emptied)! - * @param[in] pdirs the NULL terminated list of profile directories - * @returns true on success and false on failure - */ -bool -apply_to_certs_and_profiles(bool fn(char *, SECItem *), - seciteml_t **certs, char **pdirs) -{ - bool success = true; - - for (int i=0; pdirs[i] != NULL; i++) - { - seciteml_t *iter = *certs; - if (NSS_Initialize(pdirs[i], "", "", "secmod.db", 0) != SECSuccess) - { - DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdirs[i]); - continue; - } - - while (iter != NULL && iter->item != NULL) - { - SECItem *cert = iter->item; - if (! (*fn)(pdirs[i], cert)) - success = false; - iter = iter->next; - } - NSS_Shutdown(); - } - - seciteml_free(certs); - - return success; -} - -/** - * @brief Parse IPC commands from standard input. - * - * Reads command lines (R: and I:) from standard input and puts the - * certificates to process in two SECItem lists holding the - * certificates in DER format. - * @param[inout] stream from standard input - * @param[inout] install_list list of SECItems with certifiactes to install - * @param[inout] remove_list list of SECItems with certifiactes to remove - */ -static void -parse_commands (FILE *stream, - seciteml_t **install_list, seciteml_t **remove_list) -{ - char inpl[LINEBUFLEN]; - size_t inpllen; - bool parserr = true; - SECItem secitem; - - while ( fgets(inpl, LINEBUFLEN, stream) != NULL ) - { - inpllen = strnlen(inpl, LINEBUFLEN); - /* Validate input line: - * - must be (much) longer than 3 characters - * - must start with "*:" - */ - if ((inpllen > 3) && (inpl[1] == ':')) - /* Now parse Input */ - switch(inpl[0]) - { - case 'R': - parserr = true; - DEBUGPRINTF("Request to remove certificate: %s\n", &inpl[2]); - if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem)) - { - seciteml_push(remove_list, &secitem); - parserr = false; - } - break; - case 'I': - parserr = true; - DEBUGPRINTF("Request to install certificate: %s\n", &inpl[2]); - if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem)) - { - seciteml_push(install_list, &secitem); - parserr = false; - } - break; - default: - parserr = true; - } - else - { - parserr = true; - } - - if (parserr) - { - ERRORPRINTF("FATAL: Invalid input: %s\n", inpl); - exit(ERR_MOZ_INVALID_INPUT); - } - } -} - -#ifdef DO_RELEASE_BUILD -bool g_debug = false; -#else -bool g_debug = true; -#endif - -int -main (int argc, char **argv) -{ - char **dbdirs; - seciteml_t *certs_to_remove = NULL; - seciteml_t *certs_to_add = NULL; - FILE *input_stream; - - switch (argc) - { - case 1: - DEBUGPRINTF("Opening STDIN for input...\n"); - input_stream = stdin; - break; - case 2: - if (strcmp(argv[1], "--debug") == 0) - { - g_debug = true; - DEBUGPRINTF("Opening STDIN for input...\n"); - input_stream = stdin; - break; - } - case 3: - DEBUGPRINTF("Opening %s for input...\n", argv[1]); - if ((input_stream = fopen(argv[1], "r")) == NULL) - { - ERRORPRINTF ("FATAL: Could not open %s for reading!\n", - argv[1]); - exit_code = ERR_MOZ_FAILED_TO_OPEN_INPUT; - goto exit; - } - if (argc == 3 && strcmp(argv[2], "--debug") == 0) - { - g_debug = true; - } - break; - default: - ERRORPRINTF("FATAL: Wrong number of arguments!\n"); - exit_code = ERR_MOZ_WRONG_ARGC; - goto exit; - } - - dbdirs = - get_all_nssdb_dirs(); - - if (dbdirs != NULL) - { - parse_commands(input_stream, &certs_to_add, &certs_to_remove); - -#ifdef DEBUGOUTPUT - DEBUGPRINTF("OLD List of installed certs:\n"); - for (int i=0; dbdirs[i] != NULL; i++) - DEBUG_nss_list_certs(dbdirs[i]); -#endif - - if (! apply_to_certs_and_profiles(remove_cert, &certs_to_remove, dbdirs)) - exit_code |= WARN_MOZ_COULD_NOT_REMOVE_CERT; - - if (! apply_to_certs_and_profiles(import_cert, &certs_to_add, dbdirs)) - exit_code |= WARN_MOZ_COULD_NOT_ADD_CERT; - -#ifdef DEBUGOUTPUT - DEBUGPRINTF("NEW List of installed certs:\n"); - for (int i=0; dbdirs[i] != NULL; i++) - DEBUG_nss_list_certs(dbdirs[i]); -#endif - -#ifndef WIN32 - if (is_elevated()) - { - make_defaults_readable(); - } -#endif - - strv_free(dbdirs); - } - - fclose(input_stream); - -exit: - exit(exit_code); -} diff -r f8ad8fc464db -r 0651afddb4ec cinst/nss-installer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cinst/nss-installer.c Mon Sep 22 12:40:06 2014 +0200 @@ -0,0 +1,925 @@ +/* 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. + */ +/** + * @file + * @brief NSS store certificate installation process + * + * Reads from a file given on command line or stdin a list of + * instructions in the form: + * + * I:\
+ * R:\ + * ... + * + * With one instruction per line. the maximum size of an input + * line is 9999 characters (including the \\r\\n) at the end of the line. + * + * Certificates marked with I: will be installed and the ones + * marked with R: will be searched and if available removed from + * the databases. + * + * This tool tries to find all NSS databases the user has + * access to and to execute the instructions on all of them. + * + * If the tool is executed with a UID of 0 or with admin privileges under + * windows it will not look into the user directories but instead try + * to write the system wide defaults. + * + * If there are other processes accessing the databases the caller + * has to ensure that those are terminated before this process is + * executed. + * + * If the same certificate is marked to be installed and to be removed + * in one call the behavior is undefined. This should be avoided and + * may lead to errors. + * + * Returns 0 on success (Even when no stores where found) an error value + * as defined in errorcodes.h otherwise. + * + * Success messages are written to stdout. Errors to stderr. For logging + * purposes each installation / removal of a certificate will be reported + * with the profile name that it modified. + * + * To get more verbose output add the --debug parameter + * as the last parameter on the command line. + * + */ + +/** + * @brief Needs to be defined to get strnlen() + */ +#define _POSIX_C_SOURCE 200809L + +/* REMOVEME: */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUGPREFIX "MOZ-" +#include "logging.h" + +#include "certhelp.h" +#include "errorcodes.h" +#include "portpath.h" +#include "strhelp.h" +#include "nss-secitemlist.h" +#include "util.h" + +#ifndef _WIN32 +#define CONFDIRS ".mozilla", ".thunderbird" +/* Default installation directory of ubuntu 14.4 is respected */ +#define MOZILLA_DEFAULTS "/usr/lib/thunderbird/defaults", "/usr/lib/firefox/browser/defaults" +#define MOZILLA_DBNAMES "cert8.db", "key3.db", "secmod.db" +#define NSSSHARED ".pki/nssdb" +#define NSSSHARED_GLOBAL "/etc/skel/.pki/nssdb" +#define TARGET_LINUX 1 +#define DIRSEP "/" +#else +#define MOZILLA_DEFAULTS "Mozilla Firefox\\browser\\defaults", "Mozilla Thunderbird\\defaults" +#define MOZILLA_DBNAMES NULL +#define CONFDIRS "Mozilla", "Thunderbird" +#define NSSSHARED "" +#define TARGET_LINUX NULL +#define DIRSEP "\\" +#endif + +/** + * @brief Length of string buffers used + * + * The maximal length of input is defined as 9999 (+ terminating \0). + * We use it for other other input puffers besides the IPC input, too. + * (One size fits all). + */ +#define LINEBUFLEN 10000 + +#ifdef _WIN32 +#define STRTOK_R strtok_s +#else +#define STRTOK_R strtok_r +#endif + +/** + * @brief Global Return Code + * + * This will be retuned by the programm and might be set to an + * error code on fatal errors and to and warning code on non-fatal + * errors. In case of mor than one warning the warning codes will be + * ORed together. + */ +int exit_code = 0; + +/** + * @brief Return configuration base directory. + * @returns A pointer to a string containing the path to the base + * directory holding the configuration directories for e.g. mozilla + * and thunderbird. + */ +static char * +get_conf_basedir() +{ + char *cdir, *envvar; + + if (TARGET_LINUX) + envvar = "HOME" ; + else + envvar = "APPDATA"; + + if ((cdir = getenv(envvar)) != NULL) + return cdir; + else + { + ERRORPRINTF("FATAL! No %s in environment.\n", envvar); + exit(ERR_MOZ_HOMELESS); + } +} + +/** + * @brief Get a list of all mozilla profile directories + * + * Parse the profiles.ini and extract all profile paths from that. + * The expected data is in the form: + * + * [Profile99]
+ * IsRelative=1
+ * Path=Example/foo.bar + * + * or
+ * [Profile0]
+ * IsRelative=0
+ * Path=c:\\foo\\bar\\baz + * + * Mozilla also accepts the ini file on Windows even if it is UTF-16 + * encoded but never writes UTF-16 on its own. So currently we ignore + * this special case. + * + * @param[in] inifile_name path of the profile.ini to read. + * @return NULL terminated array of strings containing containing the + * absolute path of the profile directories. The array needs to + * be freed by the caller. + */ +static char ** +get_profile_dirs (char *inifile_name) +{ + char **dirs = NULL; + char *inifile_dirname; + FILE *inifile; + char line[LINEBUFLEN]; + char *key; + char *value; + char *path = NULL; + char *fqpath; + bool inprofile = false; + bool relative_path = false; + char *saveptr; + + if ((inifile = fopen(inifile_name, "r")) != NULL) + { + DEBUGPRINTF("Searching for profile paths in: '%s'\n", inifile_name); + + inifile_dirname = port_dirname(inifile_name); + while (fgets(line, LINEBUFLEN, inifile) != NULL) + { + /* Determine if we are in an profile section */ + if (str_starts_with(line, "[Profile")) + { + relative_path = false; + inprofile = true; + } + else if (line[0] == '[') + inprofile = false; + + /* If we are in a profile parse path related stuff */ + if (inprofile) + { + saveptr = NULL; + key = STRTOK_R(line, "=", &saveptr); + value = STRTOK_R(NULL, "=", &saveptr); + str_trim(&value); + if (str_equal(key, "Path")) + { + if (relative_path) + xasprintf(&path, "%s/%s", inifile_dirname, value); + else + xasprintf(&path, "%s", value); + if ((fqpath = port_realpath(path)) != NULL) + { + DEBUGPRINTF("Found profile path: '%s'\n", fqpath); + strv_append(&dirs, fqpath, strlen(fqpath)); + free (fqpath); + } + else + { + DEBUGPRINTF("WARN! Non existent profile path: '%s'\n", path); + exit_code |= WARN_MOZ_PROFILE_DOES_NOT_EXIST; + } + free(path); + } + else if (str_equal(key, "IsRelative") && + str_starts_with(value, "1")) + relative_path = true; + } + } + fclose(inifile); + } + else + { + DEBUGPRINTF("WARN! Could not open ini file: '%s'\n", inifile_name); + exit_code |= WARN_MOZ_FAILED_TO_OPEN_INI; + } + return dirs; +} + +/** + * @brief Search for mozilla profiles.ini files + * + * Use well known paths and heuristics to find the current users + * profiles.ini files on GNU/Linux and Windows systems. + * + * @return NULL terminated array of strings containing the absolute + * path of the profiles.ini files. The array needs to be freed by the + * caller. + */ +static char ** +get_profile_inis () +{ + char **inis = NULL; + char *mozpath, *fqpath, *subpath, *ppath; + DIR *mozdir; + struct dirent *mozdirent; + char *confbase = get_conf_basedir(); + const char *confdirs[] = { CONFDIRS, NULL }; + + for (int i=0; confdirs[i] != NULL; i++) + { + xasprintf(&mozpath,"%s/%s", confbase, confdirs[i]); + if ((mozdir = opendir(mozpath)) != NULL) + { + while ((mozdirent = readdir(mozdir)) != NULL) + { + xasprintf(&subpath, "%s/%s/%s", + confbase, + confdirs[i], + mozdirent->d_name); + if (port_isdir(subpath) + && (strcmp(mozdirent->d_name, "..") != 0)) + { + xasprintf(&ppath, "%s/%s/%s/%s", + confbase, + confdirs[i], + mozdirent->d_name, + "profiles.ini"); + DEBUGPRINTF("checking for %s...\n", ppath); + if ((fqpath = port_realpath(ppath)) != NULL) + { + strv_append(&inis, fqpath, strlen(fqpath)); + DEBUGPRINTF("Found mozilla ini file: '%s'\n", fqpath); + free(fqpath); + } + free(ppath); + } + free(subpath); + } + closedir(mozdir); + } + else + { + DEBUGPRINTF("Could not open %s/%s\n", confbase, confdirs[i]); + } + free(mozpath); + } + if (inis == NULL) + { + DEBUGPRINTF("No ini files found - will do nothing!\n"); + } + return inis; +} + + +/** @brief make the default nss databases readable. + * + * This uses the static paths definied in this code to ensure + * that only the defaults are touched. + * + */ +#ifndef WIN32 +static void +make_defaults_readable() +{ + const char *confdirs[] = { MOZILLA_DEFAULTS, NULL }; + const char *filenames[] = { MOZILLA_DBNAMES, NULL }; + + mode_t access_mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + for (int i=0; confdirs[i] != NULL; i++) + { + for (int j=0; filenames[j] != NULL; j++) + { + char *realpath = NULL, + *path = NULL; + xasprintf (&path, "%s/profile/%s", confdirs[i], filenames[j]); + realpath = port_realpath(path); + xfree(path); + if (!realpath) + { + syslog_error_printf("Failed to find %s \n", realpath); + continue; + } + if (chmod(realpath, access_mask)) + { + syslog_error_printf("Failed to set access_mask on file.\n"); + } + xfree (realpath); + } + } +} +#endif + +/** + * @brief Collect the default profile directories for mozilla software + * + * If the default directory is found but not the profiles subdirectory + * this will create the profiles subdirectory. + * + * @return NULL terminated array of strings containing the absolute path + * to the default profile directories. Needs to be freed by the caller. + */ +static char** +get_default_profile_dirs() +{ + char **retval = NULL; + + const char *confdirs[] = { MOZILLA_DEFAULTS, NULL }; + +#ifdef _WIN32 + char *program_files = get_program_files_folder(); + if (!program_files) + { + ERRORPRINTF ("Failed to look up program files folder.\n"); + return NULL; + } +#endif + + for (int i=0; confdirs[i] != NULL; i++) + { + char *realpath = NULL, + *profile_dir = NULL; +#ifndef _WIN32 + realpath = port_realpath(confdirs[i]); +#else + /* As on linux we only respect the default installation directory + mozilla firefox and thunderbird change their registry key with + each version as the key includes the version number. It would + be error prone to search the system for every instance. So we + only check the default installation directories. */ + xasprintf(&realpath, "%s" DIRSEP "%s", program_files, confdirs[i]); +#endif + if (realpath == NULL) + { + DEBUGPRINTF ("Did not find directory: '%s'\n", confdirs[i]); + continue; + } + xasprintf(&profile_dir, "%s" DIRSEP "profile", realpath); + xfree(realpath); + if (port_isdir(profile_dir)) + { + DEBUGPRINTF("Found default directory: '%s'\n", profile_dir); + /* All is well */ + strv_append (&retval, profile_dir, strlen(profile_dir)); + xfree(profile_dir); + profile_dir = NULL; + continue; + } + else + { + /* Create the directory */ + if (port_fileexits(profile_dir)) + { + DEBUGPRINTF ("Path: '%s' is not a directory but it exists. Skipping.\n", + profile_dir); + xfree(profile_dir); + profile_dir = NULL; + continue; + } + else + { + /* Lets create it */ + if (!port_mkdir_p(profile_dir, true)) + { + ERRORPRINTF ("Failed to create directory: '%s'\n", profile_dir); + xfree(profile_dir); + profile_dir = NULL; + continue; + } + strv_append (&retval, profile_dir, strlen(profile_dir)); + xfree(profile_dir); + profile_dir = NULL; + } + } + } +#ifdef WIN32 + xfree (program_files); +#endif + return retval; +} + +/** + * @brief Collect all mozilla profile directories of current user. + * @return NULL terminated array of strings containing the absolute + * path of the profile directories. The array needs to be freed by the + * caller. + */ +static char** +get_all_nssdb_dirs() +{ + char **mozinis, **pdirs; + char **alldirs = NULL; + + if (is_elevated()) + { +#ifndef _WIN32 + /* NSS Shared db does not exist under windows. */ + if (!port_mkdir_p(NSSSHARED_GLOBAL, false)) + { + ERRORPRINTF("Failed to create nssshared skeleton directory. \n"); + } + else + { + strv_append(&alldirs, "sql:" NSSSHARED_GLOBAL, strlen("sql:" NSSSHARED_GLOBAL)); + } +#endif + pdirs = get_default_profile_dirs(); + if (pdirs != NULL) + { + for (int i=0; pdirs[i] != NULL; i++) + { + strv_append(&alldirs, pdirs[i], strlen(pdirs[i])); + } + strv_free(pdirs); + } + return alldirs; + } + /* Search Mozilla/Firefox/Thunderbird profiles */ + if ((mozinis = get_profile_inis()) != NULL) + { + for (int i=0; mozinis[i] != NULL; i++) + { + pdirs = + get_profile_dirs(mozinis[i]); + if (pdirs != NULL) + { + for (int i=0; pdirs[i] != NULL; i++) + { + strv_append(&alldirs, pdirs[i], strlen(pdirs[i])); + } + strv_free(pdirs); + } + } + strv_free(mozinis); + } + /* Search for NSS shared DB (used by Chrome/Chromium on GNU/Linux) */ + if (TARGET_LINUX) + { + char *path, *fqpath, *sqlpath; + xasprintf(&path, "%s/%s", get_conf_basedir(), NSSSHARED); + if ((fqpath = port_realpath(path)) != NULL) + { + xasprintf(&sqlpath, "sql:%s", fqpath); + strv_append(&alldirs, sqlpath, strlen(sqlpath)); + free(sqlpath); + free(fqpath); + } + free(path); + } + return alldirs; +} + +#ifdef DEBUGOUTPUT +/** + * @brief list certificates from nss certificate store + * @param[in] confdir the directory with the certificate store + */ +static void +DEBUG_nss_list_certs (char *confdir) +{ + CERTCertList *list; + CERTCertListNode *node; + char *name; + + if (NSS_Initialize(confdir, "", "", "secmod.db", NSS_INIT_READONLY) + == SECSuccess) + { + DEBUGPRINTF("Listing certs in \"%s\"\n", confdir); + list = PK11_ListCerts(PK11CertListAll, NULL); + for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + node = CERT_LIST_NEXT(node)) + { + name = node->appData; + + DEBUGPRINTF("Found certificate \"%s\"\n", name); + } + /* According to valgrind this leaks memory in the list. + We could not find API documentation to better free this + so we accept the leakage here in case of debug. */ + CERT_DestroyCertList(list); + NSS_Shutdown(); + } + else + { + DEBUGPRINTF("Could not open nss certificate store in %s!\n", confdir); + } +} +#endif + +/** + * @brief Create a string with the name for cert in SECItem. + * + * Should be freed by caller. + * @param[in] secitemp ponts to an SECItem holding the DER certificate. + * @returns a string of the from "CN of Subject - O of Subject" + */ +static char * +nss_cert_name(SECItem *secitemp) +{ + char *cn_str, *o_str, *name; + size_t name_len; + cn_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_CN); + o_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_O); + if (!cn_str || !o_str) + { + ERRORPRINTF("FATAL: Could not parse certificate!"); + exit(ERR_INVALID_CERT); + } + name_len = strlen(cn_str) + strlen(o_str) + 4; + name = (char *)xmalloc(name_len); + snprintf(name, name_len, "%s - %s", cn_str, o_str); + free(cn_str); + free(o_str); + return name; +} + +/** + * @brief Convert a base64 encoded DER certificate to SECItem + * @param[in] b64 pointer to the base64 encoded certificate + * @param[in] b64len length of the base64 encoded certificate + * @param[out] secitem pointer to the SECItem in which to store the + * raw DER certifiacte. + * @returns true on success and false on failure + */ +static bool +base64_to_secitem(char *b64, size_t b64len, SECItem *secitem) +{ + unsigned char *dercert = NULL; + size_t dercertlen; + + if ((str_base64_decode((char **)(&dercert), &dercertlen, + b64, b64len) == 0) && + (dercertlen > 0)) + { + secitem->data = dercert; + secitem->len = (unsigned int) dercertlen; + return true; + } + else + { + DEBUGPRINTF("Base64 decode failed for: %s\n", b64); + } + return false; +} + +/** + * @brief Store DER certificate in mozilla store. + * @param[in] pdir the mozilla profile directory with the certificate + * store to manipulate. + * @param[in] dercert pointer to a SECItem holding the DER certificate + * to install + * @returns true on success and false on failure + */ +static bool +import_cert(char *pdir, SECItem *dercert) +{ + PK11SlotInfo *pk11slot = NULL; + CERTCertTrust *trust = NULL; + CERTCertificate *cert = NULL; + bool success = false; + char *cert_name = nss_cert_name(dercert); + + DEBUGPRINTF("INSTALLING cert: '%s' to: %s\n", cert_name, pdir); + pk11slot = PK11_GetInternalKeySlot(); + cert = CERT_DecodeCertFromPackage((char *)dercert->data, + (int)dercert->len); + trust = (CERTCertTrust *)xmalloc(sizeof(CERTCertTrust)); + CERT_DecodeTrustString(trust, "C,C,C"); + if (PK11_ImportCert(pk11slot, cert, CK_INVALID_HANDLE, + cert_name, PR_FALSE) == SECSuccess) + { + if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess) + { + log_certificate_der (pdir, dercert->data, dercert->len, true); + success = true; + } + } + /* This could have happened on either the import cert or + the cert change trust. If Import Cert fails with that + error the certificate has in fact been added but with + random trist bits. See NSS Bug 595861. + Reference code can be found in gnome evolution under + smime/lib/e-cert-db.c */ + if(PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) + { + if (PK11_NeedUserInit (pk11slot)) + { + PK11_InitPin (pk11slot, "", ""); + } + if (PK11_Authenticate (pk11slot, PR_TRUE, NULL) != SECSuccess) + { + DEBUGPRINTF("Failed to authenticate.\n"); + } + else if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess) + { + log_certificate_der (pdir, dercert->data, dercert->len, true); + success = true; + } + } + + if (!success) + { + DEBUGPRINTF("Failed to install certificate '%s' to '%s'!\n", cert_name, pdir); + ERRORPRINTF("Error installing certificate err: %i\n", PORT_GetError()); + } + CERT_DestroyCertificate (cert); + free(trust); + PK11_FreeSlot(pk11slot); + + free(cert_name); + return success; +} + +/** + * @brief Remove DER certificate from mozilla store. + * @param[in] pdir the mozilla profile directory with the certificate + * store to manipulate. + * @param[in] dercert pointer to a SECItem holding the DER certificate + * to remove + * @returns true on success and false on failure + */ +static bool +remove_cert(char *pdir, SECItem *dercert) +{ + PK11SlotInfo *pk11slot = NULL; + bool success = false; + char *cert_name = nss_cert_name(dercert); + CERTCertificate *cert = NULL; + + DEBUGPRINTF("REMOVING cert: '%s' from: %s\n", cert_name, pdir); + if (NSS_Initialize(pdir, "", "", "secmod.db", 0) == SECSuccess) + { + pk11slot = PK11_GetInternalKeySlot(); + cert = PK11_FindCertFromDERCertItem(pk11slot, + dercert, NULL); + if (cert != NULL) + { + if (SEC_DeletePermCertificate(cert) == SECSuccess) + { + success = true; + log_certificate_der (pdir, dercert->data, dercert->len, false); + } + else + { + DEBUGPRINTF("Failed to remove certificate '%s' from '%s'!\n", cert_name, pdir); + } + CERT_DestroyCertificate(cert); + } + else + { + DEBUGPRINTF("Could not find Certificate '%s' in store '%s'.\n", cert_name, pdir); + } + PK11_FreeSlot(pk11slot); + NSS_Shutdown(); + } + else + { + DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdir); + } + free(cert_name); + return success; +} + +/** + * @brief Apply a function to a list of certificates and profiles + * + * The function must have the signature: + * + * bool function(char *pdir, SECItem der_cert) + * + * where pdir is the path of an profile and der_cert is an raw DER + * formatted certificate. The function must return true on success + * and false on failure. + * + * This function is intended for use with the import_cert and + * remove_cert functions. + * + * @param[in] fn the function to apply + * @param[inout] certs a secitem list holding the certificates + * the list will be change (emptied)! + * @param[in] pdirs the NULL terminated list of profile directories + * @returns true on success and false on failure + */ +bool +apply_to_certs_and_profiles(bool fn(char *, SECItem *), + seciteml_t **certs, char **pdirs) +{ + bool success = true; + + for (int i=0; pdirs[i] != NULL; i++) + { + seciteml_t *iter = *certs; + if (NSS_Initialize(pdirs[i], "", "", "secmod.db", 0) != SECSuccess) + { + DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdirs[i]); + continue; + } + + while (iter != NULL && iter->item != NULL) + { + SECItem *cert = iter->item; + if (! (*fn)(pdirs[i], cert)) + success = false; + iter = iter->next; + } + NSS_Shutdown(); + } + + seciteml_free(certs); + + return success; +} + +/** + * @brief Parse IPC commands from standard input. + * + * Reads command lines (R: and I:) from standard input and puts the + * certificates to process in two SECItem lists holding the + * certificates in DER format. + * @param[inout] stream from standard input + * @param[inout] install_list list of SECItems with certifiactes to install + * @param[inout] remove_list list of SECItems with certifiactes to remove + */ +static void +parse_commands (FILE *stream, + seciteml_t **install_list, seciteml_t **remove_list) +{ + char inpl[LINEBUFLEN]; + size_t inpllen; + bool parserr = true; + SECItem secitem; + + while ( fgets(inpl, LINEBUFLEN, stream) != NULL ) + { + inpllen = strnlen(inpl, LINEBUFLEN); + /* Validate input line: + * - must be (much) longer than 3 characters + * - must start with "*:" + */ + if ((inpllen > 3) && (inpl[1] == ':')) + /* Now parse Input */ + switch(inpl[0]) + { + case 'R': + parserr = true; + DEBUGPRINTF("Request to remove certificate: %s\n", &inpl[2]); + if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem)) + { + seciteml_push(remove_list, &secitem); + parserr = false; + } + break; + case 'I': + parserr = true; + DEBUGPRINTF("Request to install certificate: %s\n", &inpl[2]); + if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem)) + { + seciteml_push(install_list, &secitem); + parserr = false; + } + break; + default: + parserr = true; + } + else + { + parserr = true; + } + + if (parserr) + { + ERRORPRINTF("FATAL: Invalid input: %s\n", inpl); + exit(ERR_MOZ_INVALID_INPUT); + } + } +} + +#ifdef DO_RELEASE_BUILD +bool g_debug = false; +#else +bool g_debug = true; +#endif + +int +main (int argc, char **argv) +{ + char **dbdirs; + seciteml_t *certs_to_remove = NULL; + seciteml_t *certs_to_add = NULL; + FILE *input_stream; + + switch (argc) + { + case 1: + DEBUGPRINTF("Opening STDIN for input...\n"); + input_stream = stdin; + break; + case 2: + if (strcmp(argv[1], "--debug") == 0) + { + g_debug = true; + DEBUGPRINTF("Opening STDIN for input...\n"); + input_stream = stdin; + break; + } + case 3: + DEBUGPRINTF("Opening %s for input...\n", argv[1]); + if ((input_stream = fopen(argv[1], "r")) == NULL) + { + ERRORPRINTF ("FATAL: Could not open %s for reading!\n", + argv[1]); + exit_code = ERR_MOZ_FAILED_TO_OPEN_INPUT; + goto exit; + } + if (argc == 3 && strcmp(argv[2], "--debug") == 0) + { + g_debug = true; + } + break; + default: + ERRORPRINTF("FATAL: Wrong number of arguments!\n"); + exit_code = ERR_MOZ_WRONG_ARGC; + goto exit; + } + + dbdirs = + get_all_nssdb_dirs(); + + if (dbdirs != NULL) + { + parse_commands(input_stream, &certs_to_add, &certs_to_remove); + +#ifdef DEBUGOUTPUT + DEBUGPRINTF("OLD List of installed certs:\n"); + for (int i=0; dbdirs[i] != NULL; i++) + DEBUG_nss_list_certs(dbdirs[i]); +#endif + + if (! apply_to_certs_and_profiles(remove_cert, &certs_to_remove, dbdirs)) + exit_code |= WARN_MOZ_COULD_NOT_REMOVE_CERT; + + if (! apply_to_certs_and_profiles(import_cert, &certs_to_add, dbdirs)) + exit_code |= WARN_MOZ_COULD_NOT_ADD_CERT; + +#ifdef DEBUGOUTPUT + DEBUGPRINTF("NEW List of installed certs:\n"); + for (int i=0; dbdirs[i] != NULL; i++) + DEBUG_nss_list_certs(dbdirs[i]); +#endif + +#ifndef WIN32 + if (is_elevated()) + { + make_defaults_readable(); + } +#endif + + strv_free(dbdirs); + } + + fclose(input_stream); + +exit: + exit(exit_code); +} diff -r f8ad8fc464db -r 0651afddb4ec cinst/nssstore_linux.c --- a/cinst/nssstore_linux.c Mon Sep 22 12:38:06 2014 +0200 +++ b/cinst/nssstore_linux.c Mon Sep 22 12:40:06 2014 +0200 @@ -27,7 +27,7 @@ #include "strhelp.h" #include "util.h" -#define NSS_PROCESS_NAME "mozilla" +#define NSS_PROCESS_NAME "trustbridge-nss-installer" /**@brief Start the process to install / remove diff -r f8ad8fc464db -r 0651afddb4ec cinst/nssstore_win.c --- a/cinst/nssstore_win.c Mon Sep 22 12:38:06 2014 +0200 +++ b/cinst/nssstore_win.c Mon Sep 22 12:40:06 2014 +0200 @@ -11,11 +11,11 @@ @brief Windows implementation of nssstore process control. The windows process will write an instructions file for - the mozilla process into the current users temp directory + the nss-installer process into the current users temp directory (%APPDATA%/Local/Temp/) and start the NSS installation process to exectute those instructions. If the current process is elevated the NSS process is run with a restricted token. - The execution of the mozilla process is not monitored. + The execution of the nss-installer process is not monitored. You have to refer to the system log to check which certificates were installed / removed by it. @@ -53,11 +53,11 @@ #include "binverify.h" #ifndef APPNAME -#define APPNAME L"cinst" +#define APPNAME L"trustbridge-certificate-installer" #endif /**@def The name of the nss installation process */ -#define NSS_APP_NAME L"mozilla.exe" +#define NSS_APP_NAME L"trustbridge-nss-installer.exe" #ifndef SELECTION_FILE_NAME #define SELECTION_FILE_NAME L"currently_selected.txt" diff -r f8ad8fc464db -r 0651afddb4ec common/errorcodes.h --- a/common/errorcodes.h Mon Sep 22 12:38:06 2014 +0200 +++ b/common/errorcodes.h Mon Sep 22 12:40:06 2014 +0200 @@ -32,7 +32,7 @@ #define ERR_INVALID_PARAMS 11 /*********************************************************************** - * mozilla specific errors and warnings + * nss-installer specific errors and warnings * errors range from 0x0081 to 0x08F * warnings from 0x0091 to 0x0098 * Warnings might be ORed together ... diff -r f8ad8fc464db -r 0651afddb4ec packaging/create-dist-package.sh.in --- a/packaging/create-dist-package.sh.in Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/create-dist-package.sh.in Mon Sep 22 12:40:06 2014 +0200 @@ -47,8 +47,8 @@ mv @CMAKE_SOURCE_DIR@/build-i386/TrustBridge-@PROJECT_VERSION@.sh \ $TMPDIR/linux/TrustBridge-@PROJECT_VERSION@-i386.sh cp @CMAKE_SOURCE_DIR@/build-windows/ui/trustbridge.exe $TMPDIR/windows -cp @CMAKE_SOURCE_DIR@/build-windows/cinst/cinst.exe $TMPDIR/windows -cp @CMAKE_SOURCE_DIR@/build-windows/cinst/mozilla.exe $TMPDIR/windows +cp @CMAKE_SOURCE_DIR@/build-windows/cinst/trustbridge-certificate-installer.exe $TMPDIR/windows +cp @CMAKE_SOURCE_DIR@/build-windows/cinst/trustbridge-nss-installer.exe $TMPDIR/windows cp -r @CMAKE_SOURCE_DIR@/packaging/resources $TMPDIR/resources cp @CMAKE_SOURCE_DIR@/build-windows/packaging/DesktopShellRun.dll $TMPDIR/resources cp -r @CMAKE_BINARY_DIR@/manuals/help-manual/html $TMPDIR/windows/doc diff -r f8ad8fc464db -r 0651afddb4ec packaging/filelist.nsh --- a/packaging/filelist.nsh Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/filelist.nsh Mon Sep 22 12:40:06 2014 +0200 @@ -8,7 +8,7 @@ ; This file is not autogenerated. ; Keep it updated if you add new things. -File "${files_dir}${path_sep}cinst.exe" +File "${files_dir}${path_sep}trustbridge-certificate-installer.exe" File "${files_dir}${path_sep}trustbridge.exe" -File "${files_dir}${path_sep}mozilla.exe" +File "${files_dir}${path_sep}trustbridge-nss-installer.exe" File /r /x .buildinfo "${files_dir}${path_sep}doc" diff -r f8ad8fc464db -r 0651afddb4ec packaging/linux-createpackage.sh.in --- a/packaging/linux-createpackage.sh.in Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/linux-createpackage.sh.in Mon Sep 22 12:40:06 2014 +0200 @@ -39,10 +39,10 @@ fi INSTALLER="@CMAKE_BINARY_DIR@/TrustBridge-@PROJECT_VERSION@.sh" -UNINSTALLER="$TMPDIR/bin/trustbridge-deinstall.sh" +UNINSTALLER="$TMPDIR/bin/trustbridge-uninstall.sh" -EXEFILES=("@CMAKE_BINARY_DIR@/cinst/cinst" - "@CMAKE_BINARY_DIR@/cinst/mozilla" +EXEFILES=("@CMAKE_BINARY_DIR@/cinst/trustbridge-certificate-installer" + "@CMAKE_BINARY_DIR@/cinst/trustbridge-nss-installer" "@CMAKE_BINARY_DIR@/ui/trustbridge" "@CMAKE_BINARY_DIR@/ui/trustbridge-tray-starter.sh" "$UNINSTALLER") diff -r f8ad8fc464db -r 0651afddb4ec packaging/linux-installer-uninstall.inc --- a/packaging/linux-installer-uninstall.inc Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/linux-installer-uninstall.inc Mon Sep 22 12:40:06 2014 +0200 @@ -31,23 +31,27 @@ setup_cronjob deinstall } -deinstall_certs() +deinstall_certs_and_config() { - local cinst="${oldinstcfg[PREFIX]}/bin/cinst" + local cinst="${oldinstcfg[PREFIX]}/bin/trustbridge-certificate-installer" local certlist=`ls 2>/dev/null -1 ${instdata_path}/list-installed.txt` getxt "Uninstalling certificates ...\n" - if [ "$certlist" ] ; then + if [ -f "$certlist" ] ; then getxt "Using certificate list '%s'.\n" "$certlist" if [ -x "$cinst" ] ; then "$cinst" "list=$certlist" "choices=uninstall" else getxt >&2 "WARNING: can't execute %s for certificate deinstallation.\n" "$cinst" fi + rm_files "$certlist" else getxt "No certificate list found. Nothing to do.\n" fi + + rm_files "${instcfg_path}/TrustBridge.ini" + rm_empty_dirs "$instcfg_path" } deinstall_etc() @@ -55,22 +59,20 @@ getxt "Removing cron job ...\n" remove_cronjob - # FIXME (issue133): delete all files created by the application. - local tbcfg_files=( "${instcfg_path}/TrustBridge.ini" - "${instcfg_path}/trustbridge-tray-starter.cfg" + getxt "Removing certificate lists from: %s:\n" "$instdata_path" + rm_files "$instdata_path"/list-2*.txt + + local tbcfg_files=( "${instcfg_path}/trustbridge-tray-starter.cfg" "$instcfg_file" ) - getxt "Removing certificate lists from: %s:\n" "$instdata_path" - rm_files "$instdata_path"/list-*.txt + getxt "Removing configuration files:\n" + rm_files "${tbcfg_files[@]}" + rm_empty_dirs "$instcfg_path" getxt "Removing PID file from: %s:\n" "$instdata_path" rm_files "$instdata_path"/*.pid rm_empty_dirs "$instdata_path" - getxt "Removing configuration files:\n" - rm_files "${tbcfg_files[@]}" - rm_empty_dirs "$instcfg_path" - getxt "Removing TrustBridge from autostart\n" rm_files "${autostart_path}/trustbridge.desktop" update-desktop-database 2>&1 || true @@ -84,7 +86,11 @@ if [ "${oldinstcfg[PREFIX]}" ] ; then getxt "Really deinstall TrustBridge from '%s'? [y/n]\n" "${oldinstcfg[PREFIX]}" yorn || exit 0 - deinstall_certs + local certlist=`ls 2>/dev/null -1 ${instdata_path}/list-installed.txt` + if [ -f "$certlist" ] ; then + getxt "Do you also want to remove the certificates installed by TrustBridge? [y/n]\n" + yorn && deinstall_certs_and_config + fi local deinstdir="${oldinstcfg[PREFIX]}/bin" getxt "Deinstalling from '%s'.\n" "${oldinstcfg[PREFIX]}" for file in $BINNAMES ; do diff -r f8ad8fc464db -r 0651afddb4ec packaging/linux-installer-uninstall.l10n-de --- a/packaging/linux-installer-uninstall.l10n-de Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/linux-installer-uninstall.l10n-de Mon Sep 22 12:40:06 2014 +0200 @@ -16,4 +16,5 @@ ["Using certificate list '%s'.\n"]="Die Zertifikatsliste '%s' wird verwendet.\n" ["WARNING: Could not delete: '%s'!\n"]="WARNUNG: Konnte '%s' nicht löschen!\n" ["WARNING: can't execute %s for certificate deinstallation.\n"]="WARNUNG: '%s' kann nicht zum deinstallieren der Zertifikate ausgeführt werden.\n" + ["Do you also want to remove the certificates installed by TrustBridge? [y/n]\n"]="Sollen die von TrustBridge installierten Zertifikate auch entfernt werden? [j\n]\n" ) diff -r f8ad8fc464db -r 0651afddb4ec packaging/linux-installer.inc --- a/packaging/linux-installer.inc Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/linux-installer.inc Mon Sep 22 12:40:06 2014 +0200 @@ -51,7 +51,7 @@ getxt " -p, --prefix=PATH install files in PATH\n" getxt " -f, --force install to given prefix, even when a current\n" getxt " installation with different prefix exists.\n" - getxt " -d, --deinstall deinstall files from current installation\n" + getxt " -u, --uninstall uninstall files from current installation\n" getxt " -s, --system create a system wide (de)installation\n" getxt " --help display this help and exit\n" getxt " --version output version information and exit\n" @@ -61,7 +61,7 @@ parse_args() { OPTS=`getopt \ - -l deinstall,update,show-after-update,force,help,prefix:,system,version \ + -l uninstall,update,show-after-update,force,help,prefix:,system,version \ -o d,f,p:,s -n "$ME" -- "$@"` [ $? -eq 0 ] || usage 23 @@ -81,7 +81,7 @@ FORCE=1 shift 1 ;; - --deinstall|-d) + --uninstall|-u) DEINSTALL=1 shift 1 ;; @@ -119,8 +119,8 @@ getxt "TrustBridge has been installed to: '%s'\n\n" "${instcfg[PREFIX]}" getxt "To remove the application and the root certificates it has inserted,\n" - getxt "call the deinstall command:\n" - echo " $RUNCMD ${instcfg[PREFIX]}/bin/trustbridge-deinstall.sh" + getxt "call the uninstall command:\n" + echo " $RUNCMD ${instcfg[PREFIX]}/bin/trustbridge-uninstall.sh" echo "#################################################################################" getxt "Press enter to launch '%s'\n" "${instcfg[PREFIX]}/bin/trustbridge" @@ -296,12 +296,21 @@ getxt "An existing installation (v%s) was detected!\n" "${oldinstcfg[VERSION]}" getxt "It is HIGHLY RECOMMENDED to accept the default prefix\n" getxt "to update the current installation.\n" - getxt "For a new prefix you should deinstall first!\n" + getxt "For a new prefix you should uninstall first!\n" fi - getxt "Select installation prefix for TrustBridge [%s]: " "${inst_default_prefix}" - read -e instcfg[PREFIX] - - [ -z "${instcfg[PREFIX]}" ] && instcfg[PREFIX]="${inst_default_prefix}" + until [ "${instcfg[PREFIX]}" ] ; do + getxt "Select installation prefix for TrustBridge [%s]: " "${inst_default_prefix}" + read -e instcfg[PREFIX] + if echo "${instcfg[PREFIX]}" | grep -q '[~$"\\]' ; then + getxt "\nWARNING: The given path name contains special characters\n" + getxt "(one or more of %s )\n" "~ $ \" \\" + getxt "Please note that these characters are not expanded by the installer,\n" + getxt "the installation directory will be containing these characters verbatim.\n\n" + getxt "Do you really want to use %s as installation prefix?\n" "${instcfg[PREFIX]}" + yorn || { instcfg[PREFIX]="" ; continue ; } + fi + [ -z "${instcfg[PREFIX]}" ] && instcfg[PREFIX]="${inst_default_prefix}" + done else # Prefix was given on invocation: if [ "${oldinstcfg[PREFIX]}" -a \ diff -r f8ad8fc464db -r 0651afddb4ec packaging/linux-installer.l10n-de --- a/packaging/linux-installer.l10n-de Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/linux-installer.l10n-de Mon Sep 22 12:40:06 2014 +0200 @@ -1,10 +1,3 @@ -# 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. - L10N_DE+=( [" installation with different prefix exists.\n"]=" bereits eine Installation in einem andern Pfad existiert.\n" [" --help display this help and exit\n"]=" --help diese Hilfe anzeigen und beenden\n" @@ -13,9 +6,11 @@ [" -f, --force install to given prefix, even when a current\n"]=" -f, --force installiere in den angegebenen Installations-Pfad auch wenn\n" [" -p, --prefix=PATH install files in PATH\n"]=" -p, --prefix=PATH den Installations-Pfad PATH verwenden\n" [" -s, --system create a system wide (de)installation\n"]=" -s, --system eine systemweite (De)Installation durchführen\n" + ["(one or more of %s )\n"]="(mindesten eines von %s )\n" ["An existing installation (v%s) was detected!\n"]="Es wurde eine vorhandene Installation (v%s) gefunden!\n" ["Cleaning up temporary stuff ...\n"]="Räume temporäre Dateien auf ...\n" ["Could not create '%s'!\n"]="Konnte '%s' nicht erstellen!\n" + ["Do you really want to use %s as installation prefix?\n"]="Möchten Sie wirklich %s als Installations-Pfad verwenden?\n" ["Failed to create autostart directory: '%s'\n"]="Konnte das autostart-Verzeichnis '%s' nicht erstellen\n" ["Failed to create startmenu directory: '%s'\n"]="Konnte das Startmenü-Verzeichnis '%s' nicht erstellen\n" ["For a new prefix you should deinstall first!\n"]="Um einen neuen Installations-Pfad zu verwenden bitte erst deinstallieren!\n" @@ -25,6 +20,7 @@ ["It appears your system architecture is %s.\n"]="Dieses System scheint eine %s Architektur zu besitzen.\n" ["It is HIGHLY RECOMMENDED to accept the default prefix\n"]="Es wird DRINGEND EMPFOHLEN die Vorgabe zu akzeptieren\n" ["Options:\n"]="Optionen:\n" + ["Please note that these characters are not expanded by the installer,\n"]="Bitte beachten sie, dass diese Zeichen vom Installer nicht speziell behandelt werden,\n" ["Prefix differs from current installation (%s). Aborting!\n"]="Installations-Pfad weicht von vorhandener Installation (%s) ab.\nVorgang abgebrochen!\n" ["Preparing trustbridge-tray-starter ...\n"]="Bereite trustbridge-tray-starter vor ...\n" ["Press enter to launch '%s'\n"]="Drücken Sie die Eingabetaste um '%s' auszuführen\n" @@ -42,10 +38,12 @@ ["Usage: %s [OPTION]...\n"]="Aufruf: %s [OPTION]...\n" ["Using python uudecode provided by installer.\n"]="Verwende vom Installer bereitgestelltes python uudecode.\n" ["Writing installation configuration to: %s ...\n"]="Schreibe Installationskonfiguration nach: %s ...\n" + ["\nWARNING: The given path name contains special characters\n"]="\nWARNUNG: der angegebene Pfad enthält Sonderzeichen\n" ["call the deinstall command:\n"]="rufen Sie den De-Installationsbefehl auf:\n" ["checking for uudecode ...\n"]="Prüfe uudecode ...\n" ["creating installation directory ...\n"]="lege das Installations-Verzeichnis an ...\n" ["or press Control-C to quit the installer.\n"]="oder Drücken sie Strg-C um den Installer zu beenden.\n" + ["the installation directory will be containing these characters verbatim.\n\n"]="der Installations-Pfad wird diese Zeichen unverändert enthalten.\n\n" ["to update the current installation.\n"]="um die vorhandene Installation zu aktualisieren.\n" ["unpacking files ...\n"]="Entpacke Dateien ...\n" ) diff -r f8ad8fc464db -r 0651afddb4ec packaging/trustbridge.nsi --- a/packaging/trustbridge.nsi Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/trustbridge.nsi Mon Sep 22 12:40:06 2014 +0200 @@ -271,7 +271,7 @@ Section "Uninstall" StrCmp "$douninstcertificates" "TRUE" 0 skip_certs ExpandEnvStrings $0 %LOCALAPPDATA% - nsExec::ExecToLog '"$INSTDIR\cinst.exe" "list=$0\BSI\TrustBridge\list-installed.txt" "choices=uninstall"' + nsExec::ExecToLog '"$INSTDIR\trustbridge-certificate-installer.exe" "list=$0\BSI\TrustBridge\list-installed.txt" "choices=uninstall"' skip_certs: RMDir /r "$INSTDIR" ; TODO (issue137) include uninstall files ; !include "filelist-un.nsh" diff -r f8ad8fc464db -r 0651afddb4ec packaging/win-createpackage.sh.in --- a/packaging/win-createpackage.sh.in Mon Sep 22 12:38:06 2014 +0200 +++ b/packaging/win-createpackage.sh.in Mon Sep 22 12:40:06 2014 +0200 @@ -13,7 +13,7 @@ TMPDIR=$(mktemp -d) TMPINST=$(mktemp) -EXEFILES=$(find . -name cinst.exe -o -name trustbridge.exe -o -name mozilla.exe) +EXEFILES=$(find . -name trustbridge-certificate-installer.exe -o -name trustbridge.exe -o -name trustbridge-nss-installer.exe) HELPDIR=@CMAKE_BINARY_DIR@/manuals/help-manual/html cp $EXEFILES $TMPDIR diff -r f8ad8fc464db -r 0651afddb4ec ui/img/README.txt --- a/ui/img/README.txt Mon Sep 22 12:38:06 2014 +0200 +++ b/ui/img/README.txt Mon Sep 22 12:40:06 2014 +0200 @@ -1,9 +1,37 @@ += How to create the pixel icons. + Sources: oxygen-icons_4.13.0.orig.tar.xz -Tool: Debian,Package: inkscape Version: 0.48.3.1-1.3 +Tools: Debian Package: inkscape Version: 0.48.3.1-1.3 + Debian Package: imagemagick Version: 8:6.7.7.10-5+deb7u3 + +== update-list.png + +Was taken from the Oxygen Icon set (where rendering is done by inkscape). +prerendered in green. + +The color was adapted on the pixel version with gimp +by visial matching shades of blue in the other icons. + +Gimp tools used: +Menu -> Color -> colorize.. : + with parameters approx 210, 65, 15 +Menu -> Color -> Curves: + Adding more billiance by dragging the ligher blue color shades more apart. + So from the default curve make it go a little bit steeper for 4/5 + of the way and then less steep. + +== write-into / write-remove + +Original files: oxygen-icons_4.13.0.orig.tar.xz +./svn-commit.svgz -> write-remove +./svn-update.svgz -> write-into: + Using inkscape to move the arrow up. + +Rendered with inkscape to 48pixel height. == store-base store-base created from svn-commit.svgz, removing the arrow -and the two interal circles. +and the two internal circles. == cert-is-imported-good.svg Starting with svn-update.svg, removing the arrow and the two small circles. diff -r f8ad8fc464db -r 0651afddb4ec ui/img/cert-is-installed-bad-48.png Binary file ui/img/cert-is-installed-bad-48.png has changed diff -r f8ad8fc464db -r 0651afddb4ec ui/img/cert-is-installed-bad.svg --- a/ui/img/cert-is-installed-bad.svg Mon Sep 22 12:38:06 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8019 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r f8ad8fc464db -r 0651afddb4ec ui/img/cert-is-installed-good-48.png Binary file ui/img/cert-is-installed-good-48.png has changed diff -r f8ad8fc464db -r 0651afddb4ec ui/img/cert-is-installed-good.svg --- a/ui/img/cert-is-installed-good.svg Mon Sep 22 12:38:06 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3735 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -