Mercurial > trustbridge
diff cinst/nss-installer.c @ 1175:e210ecc32d69
(issue128) Rename mozilla process to trustbridge-nss-installer
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 22 Sep 2014 11:19:43 +0200 |
parents | cinst/mozilla.c@1e429faf7c84 |
children | 12ed0b72e9f5 |
line wrap: on
line diff
--- /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); +}