[PATCH 1 of 2] (issue128) Rename mozilla process to trustbridge-nss-installer
Wald Commits
scm-commit at wald.intevation.org
Mon Sep 22 11:34:10 CEST 2014
# HG changeset patch
# User Andre Heinecke <andre.heinecke at intevation.de>
# Date 1411377583 -7200
# Node ID e210ecc32d697d63266d70532281f51c9edab316
# Parent 7175d117e69a6d370ce45575845dc3806536fae5
(issue128) Rename mozilla process to trustbridge-nss-installer
diff -r 7175d117e69a -r e210ecc32d69 cinst/CMakeLists.txt
--- a/cinst/CMakeLists.txt Fri Sep 19 17:47:03 2014 +0200
+++ b/cinst/CMakeLists.txt Mon Sep 22 11:19:43 2014 +0200
@@ -56,47 +56,47 @@
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 7175d117e69a -r e210ecc32d69 cinst/mozilla.c
--- a/cinst/mozilla.c Fri Sep 19 17:47:03 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:\<base64 DER econded certificate\> <BR>
- * R:\<base64 DER econded certificate\>
- * ...
- *
- * 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 <unistd.h>
-
-#include <cert.h>
-#include <certdb.h>
-#include <certt.h>
-#include <dirent.h>
-#include <nss.h>
-#include <pk11pub.h>
-#include <secerr.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#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]<BR>
- * IsRelative=1<BR>
- * Path=Example/foo.bar
- *
- * or<BR>
- * [Profile0]<BR>
- * IsRelative=0<BR>
- * 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 7175d117e69a -r e210ecc32d69 cinst/nss-installer.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cinst/nss-installer.c Mon Sep 22 11:19:43 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:\<base64 DER econded certificate\> <BR>
+ * R:\<base64 DER econded certificate\>
+ * ...
+ *
+ * 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 <unistd.h>
+
+#include <cert.h>
+#include <certdb.h>
+#include <certt.h>
+#include <dirent.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <secerr.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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]<BR>
+ * IsRelative=1<BR>
+ * Path=Example/foo.bar
+ *
+ * or<BR>
+ * [Profile0]<BR>
+ * IsRelative=0<BR>
+ * 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 7175d117e69a -r e210ecc32d69 cinst/nssstore_linux.c
--- a/cinst/nssstore_linux.c Fri Sep 19 17:47:03 2014 +0200
+++ b/cinst/nssstore_linux.c Mon Sep 22 11:19:43 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 7175d117e69a -r e210ecc32d69 cinst/nssstore_win.c
--- a/cinst/nssstore_win.c Fri Sep 19 17:47:03 2014 +0200
+++ b/cinst/nssstore_win.c Mon Sep 22 11:19:43 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.
@@ -57,7 +57,7 @@
#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 7175d117e69a -r e210ecc32d69 common/errorcodes.h
--- a/common/errorcodes.h Fri Sep 19 17:47:03 2014 +0200
+++ b/common/errorcodes.h Mon Sep 22 11:19:43 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 7175d117e69a -r e210ecc32d69 packaging/create-dist-package.sh.in
--- a/packaging/create-dist-package.sh.in Fri Sep 19 17:47:03 2014 +0200
+++ b/packaging/create-dist-package.sh.in Mon Sep 22 11:19:43 2014 +0200
@@ -48,7 +48,7 @@
$TMPDIR/linux/TrustBridge- at 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-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 7175d117e69a -r e210ecc32d69 packaging/filelist.nsh
--- a/packaging/filelist.nsh Fri Sep 19 17:47:03 2014 +0200
+++ b/packaging/filelist.nsh Mon Sep 22 11:19:43 2014 +0200
@@ -10,5 +10,5 @@
File "${files_dir}${path_sep}cinst.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 7175d117e69a -r e210ecc32d69 packaging/linux-createpackage.sh.in
--- a/packaging/linux-createpackage.sh.in Fri Sep 19 17:47:03 2014 +0200
+++ b/packaging/linux-createpackage.sh.in Mon Sep 22 11:19:43 2014 +0200
@@ -42,7 +42,7 @@
UNINSTALLER="$TMPDIR/bin/trustbridge-deinstall.sh"
EXEFILES=("@CMAKE_BINARY_DIR@/cinst/cinst"
- "@CMAKE_BINARY_DIR@/cinst/mozilla"
+ "@CMAKE_BINARY_DIR@/cinst/trustbridge-nss-installer"
"@CMAKE_BINARY_DIR@/ui/trustbridge"
"@CMAKE_BINARY_DIR@/ui/trustbridge-tray-starter.sh"
"$UNINSTALLER")
diff -r 7175d117e69a -r e210ecc32d69 packaging/win-createpackage.sh.in
--- a/packaging/win-createpackage.sh.in Fri Sep 19 17:47:03 2014 +0200
+++ b/packaging/win-createpackage.sh.in Mon Sep 22 11:19:43 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 cinst.exe -o -name trustbridge.exe -o -name trustbridge-nss-installer.exe)
HELPDIR=@CMAKE_BINARY_DIR@/manuals/help-manual/html
cp $EXEFILES $TMPDIR
diff -r 7175d117e69a -r e210ecc32d69 ui/tests/CMakeLists.txt
--- a/ui/tests/CMakeLists.txt Fri Sep 19 17:47:03 2014 +0200
+++ b/ui/tests/CMakeLists.txt Mon Sep 22 11:19:43 2014 +0200
@@ -151,15 +151,15 @@
target_link_libraries(nsstest -luserenv)
endif()
- add_dependencies(nsstest mozilla)
+ add_dependencies(nsstest trustbridge-nss-installer)
- # NSS code searches the mozilla process in the same path as the caller
+ # NSS code searches the trustbridge-nss-installer process in the same path as the caller
if (WIN32)
- file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/mozilla.exe" link)
- file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/../../cinst/mozilla.exe" target)
+ file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer.exe" link)
+ file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/../../cinst/trustbridge-nss-installer.exe" target)
else()
- file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/mozilla" link)
- file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/../../cinst/mozilla" target)
+ file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/trustbridge-nss-installer" link)
+ file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/../../cinst/trustbridge-nss-installer" target)
endif()
if (NOT EXISTS ${link})
More information about the Trustbridge-commits
mailing list