Created
July 17, 2015 20:20
-
-
Save pehrlich/08852e8f7da81e136d70 to your computer and use it in GitHub Desktop.
Cross Platform Mozilla NSS Root Certificate Installation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This looks OK to release for me. You can make this public.