Skip to content

Instantly share code, notes, and snippets.

@pehrlich
Created July 17, 2015 20:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pehrlich/08852e8f7da81e136d70 to your computer and use it in GitHub Desktop.
Save pehrlich/08852e8f7da81e136d70 to your computer and use it in GitHub Desktop.
Cross Platform Mozilla NSS Root Certificate Installation
#include "stdafx.h"
#include "CertificateNSS.h"
#include "Certificate.h"
#include <boost/filesystem/operations.hpp>
#include <nss.h>
#include <cert.h>
#include <certdb.h>
ProfileLocker::ProfileLocker(const boost::filesystem::path& profilePath) : m_isValid(false)
{
GetSharedMutex().lock();
m_isValid = (NSS_InitReadWrite(profilePath.string().c_str()) == SECSuccess);
if (!m_isValid) {
GetSharedMutex().unlock();
}
}
ProfileLocker::~ProfileLocker()
{
if (m_isValid) {
NSS_Shutdown();
GetSharedMutex().unlock();
}
}
std::mutex& ProfileLocker::GetSharedMutex()
{
static std::mutex s_mutex;
return s_mutex;
}
std::vector<boost::filesystem::path> CertificateNSS::GetUserProfiles()
{
std::vector<boost::filesystem::path> profiles;
const auto path = GetProfilesDirectory();
if (!boost::filesystem::is_directory(path)) {
return profiles;
}
boost::filesystem::directory_iterator endIt;
for (boost::filesystem::directory_iterator it(path); it != endIt; ++it) {
if (boost::filesystem::is_directory(it->status())) {
profiles.push_back(it->path());
}
}
return profiles;
}
bool CertificateNSS::Install() const
{
std::string derCert = m_cert.GetBytes(CertEncoding::DER);
if (derCert.empty()) {
return false;
}
bool wasInstalled = false;
CERTCertDBHandle* certdb = CERT_GetDefaultCertDB();
if (certdb) {
SECItem cert = { siBuffer, (unsigned char*)derCert.c_str(), static_cast<unsigned int>(derCert.size()) };
SECItem* certArray[1] = { &cert };
SECCertUsage noOpUsage = certUsageUserCertImport; // Not used, but required
CERTCertificate** certificates = nullptr;
wasInstalled = (CERT_ImportCerts(certdb, noOpUsage, 1, certArray, &certificates, PR_TRUE, PR_TRUE,
const_cast<char*>(Certificate::GetNickname().c_str())) == SECSuccess);
if (certificates[0]) {
CERTCertTrust trust = { CERTDB_TRUSTED_CA | CERTDB_VALID_CA, 0, 0 };
CERT_ChangeCertTrust(certdb, certificates[0], &trust);
CERT_DestroyCertificate(certificates[0]);
}
}
return wasInstalled;
}
bool CertificateNSS::IsInstalled() const
{
std::string derCert = m_cert.GetBytes(CertEncoding::DER);
if (derCert.empty()) {
return false;
}
bool wasInstalled = false;
SECItem cert = { siBuffer, (unsigned char*)derCert.c_str(), static_cast<unsigned int>(derCert.size()) };
CERTCertDBHandle* certdb = CERT_GetDefaultCertDB();
if (certdb) {
CERTCertificate* certificate = CERT_FindCertByDERCert(certdb, &cert);
if (certificate) {
wasInstalled = true;
CERT_DestroyCertificate(certificate);
}
}
return wasInstalled;
}
bool CertificateNSS::Uninstall() const
{
std::string derCert = m_cert.GetBytes(CertEncoding::DER);
if (derCert.empty()) {
return false;
}
bool wasUninstalled = false;
SECItem cert = { siBuffer, (unsigned char*)derCert.c_str(), static_cast<unsigned int>(derCert.size()) };
CERTCertDBHandle* certdb = CERT_GetDefaultCertDB();
if (certdb) {
CERTCertificate* certificate = CERT_FindCertByDERCert(certdb, &cert);
if (certificate) {
wasUninstalled = (SEC_DeletePermCertificate(certificate) == SECSuccess);
CERT_DestroyCertificate(certificate);
}
}
return wasUninstalled;
}
bool CertificateNSS::UninstallAll()
{
bool wasUninstalled = true;
CERTCertDBHandle* certdb = CERT_GetDefaultCertDB();
if (!certdb) {
return true;
}
// Delete up to 100 profiles
for (int i = 0; i < 100; i++) {
bool failed = true;
CERTCertificate* certificate = CERT_FindCertByNickname(certdb, Certificate::GetNickname().c_str());
if (certificate) {
wasUninstalled = (SEC_DeletePermCertificate(certificate) == SECSuccess);
if (wasUninstalled) {
failed = false;
}
CERT_DestroyCertificate(certificate);
}
if (failed) {
break;
}
}
return wasUninstalled;
}
#pragma once
#include <mutex>
#include <boost/filesystem/path.hpp>
class Certificate;
/// <summary>
/// Create exclusive access to a NSS profile
/// </summary>
class ProfileLocker {
public:
/// <summary>
/// Create exclusive access to the provided profile path
/// </summary>
/// <remarks>
/// The profilePath should be one of the paths provided in the vector
/// returned by the CertificateNSS::GetUserProfiles() function.
/// </remarks>
ProfileLocker(const boost::filesystem::path& profilePath);
~ProfileLocker();
/// <summary>
/// Indicate whether we are able to manipulate the profile
/// </summary>
bool IsValid() const { return m_isValid; }
private:
bool m_isValid;
static std::mutex& GetSharedMutex();
};
class CertificateNSS {
public:
CertificateNSS(const Certificate& cert):
m_cert(cert)
{}
private:
const Certificate& m_cert;
public:
/// <summary>
/// Get the directory containing the current user's NSS profiles
/// </summary>
static boost::filesystem::path GetProfilesDirectory();
/// <summary>
/// Retrieves a vector of paths to the current user's NSS profile directories
/// </summary>
/// <remarks>
/// A profile may be locked by creating an instance of the ProfileLocker
/// class using one of the paths from the vector. The NSS profile
/// installation functions must only be called while a ProfileLocker
/// instance is in scope.
/// </remarks>
static std::vector<boost::filesystem::path> GetUserProfiles();
/// <summary>
/// Install this certificate for use by at least one of the current user's NSS profiles
/// </summary>
/// <remarks>
/// A profile must be locked before calling this function. See GetUserProfiles().
/// </remarks>
bool Install() const;
/// <summary>
/// Indicates whether or not this certificate is available for use by at
/// least one of the current user's NSS certificate databases
/// </summary>
/// <remarks>
/// A profile must be locked before calling this function. See GetUserProfiles().
/// </remarks>
bool IsInstalled() const;
/// <summary>
/// Uninstall this certificate from the current user's NSS profiles
/// <summary>
/// <remarks>
/// A profile must be locked before calling this function. See GetUserProfiles().
/// </remarks>
bool Uninstall() const;
/// <summary>
/// Attempts to uninstall all certificates matching our nickname from the
/// current user's profile
/// </summary>
/// <remarks>
/// A return value of true means that no more certificates exist. A return
/// value of false indicates that some or all of the certificates remain.
/// A profile must be locked before calling this function. See GetUserProfiles().
/// </remarks>
static bool UninstallAll();
};
#include "stdafx.h"
#include "CertificateNSS.h"
boost::filesystem::path CertificateNSS::GetProfilesDirectory()
{
static std::once_flag s_flag;
static boost::filesystem::path s_profilesPath;
std::call_once(s_flag, [] {
const char* home = getenv("HOME");
s_profilesPath = home ? home : "~";
s_profilesPath += "/Library/Application Support/Firefox/Profiles/";
});
return s_profilesPath;
}
#include "stdafx.h"
#include "CertificateNSS.h"
boost::filesystem::path CertificateNSS::GetProfilesDirectory()
{
static std::once_flag s_flag;
static boost::filesystem::path s_profilesPath;
std::call_once(s_flag, [] {
const char* home = getenv("HOME");
s_profilesPath = home ? home : "~";
s_profilesPath += "/.mozilla/firefox/";
});
return s_profilesPath;
}
#include "stdafx.h"
#include "CertificateNSS.h"
boost::filesystem::path CertificateNSS::GetProfilesDirectory()
{
static std::once_flag s_flag;
static boost::filesystem::path s_profilesPath;
std::call_once(s_flag, [] {
const char* appData = getenv("APPDATA");
s_profilesPath = appData ? appData : "C:";
s_profilesPath += "\\Mozilla\\Firefox\\Profiles\\";
});
return s_profilesPath;
}
@codemercenary
Copy link

This looks OK to release for me. You can make this public.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment