[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