Skip to content

Commit

Permalink
feat(trust_store): incomplete implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhexists committed Sep 2, 2024
1 parent a8ee68f commit 00fd8f3
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 38 deletions.
21 changes: 21 additions & 0 deletions core/src/commands/generate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
trust_stores::{CAValue, NSSValue},
utils::{get_certificates_from_data_dir, save_generated_cert_key_files},
x509::{
ca_cert::CACert, ca_req::CAReq, distinguished_name::DistinguishedName, leaf_cert::LeafCert,
Expand All @@ -25,6 +26,7 @@ pub fn generate(
state: Option<String>,
output: Option<String>,
request: bool,
install: bool,
) -> Result<(), Box<dyn std::error::Error>> {
if request {
for domain in &domains {
Expand Down Expand Up @@ -189,6 +191,14 @@ pub fn generate(
}
}
}
if install {
let trust_store_object: CAValue = CAValue {
certificate: cert.clone(),
};
CAValue::install_certificate(&trust_store_object)?;
let nss_store_object: NSSValue = NSSValue { certificate: cert };
NSSValue::install_certificate(&nss_store_object)?;
}
return Ok(());
} else {
eprintln!("Corresponding KeyFile Not Found");
Expand Down Expand Up @@ -303,6 +313,17 @@ pub fn generate(
}
}
}

if install {
let trust_store_object: CAValue = CAValue {
certificate: d_cert.clone(),
};
CAValue::install_certificate(&trust_store_object)?;
let nss_store_object: NSSValue = NSSValue {
certificate: d_cert,
};
NSSValue::install_certificate(&nss_store_object)?;
}
} else {
if noca {
eprintln!("Error: No CA Certificates found and generation of a new one is disabled by `--no-ca`");
Expand Down
11 changes: 10 additions & 1 deletion core/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ enum Commands {

#[arg(long = "req-only")]
request: bool,

#[arg(short = 'i', long = "install")]
install: bool,
},
}

Expand All @@ -77,6 +80,7 @@ fn main() {
state,
output,
request,
install,
} => {
if certfile.is_some() != keyfile.is_some() {
if certfile.is_some() {
Expand All @@ -96,9 +100,14 @@ fn main() {
std::process::exit(1);
}

if request && install {
eprint!("Error: `--req-only` and `csr` are incompatible. You can't generate requests from a request certificate.");
std::process::exit(1);
}

let _ = generate(
domains, noca, csr, certfile, keyfile, country, commonname, state, output,
request,
request, install,
);
}
}
Expand Down
60 changes: 60 additions & 0 deletions core/src/trust_stores/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use openssl::error::ErrorStack;
use std::{error::Error, fmt, io};

#[derive(Debug)]
pub enum TrustStoreError {
PEMFileCreationError(io::Error),
PEMEncodingError(ErrorStack),
WriteToFileError(io::Error),
}

impl fmt::Display for TrustStoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PEMEncodingError(err) => {
write!(f, "Trust Store PEM Encoding Failed: {}", err)
}
Self::PEMFileCreationError(err) => {
write!(f, "Trust Store PEM File creation Failed: {}", err)
}
Self::WriteToFileError(err) => {
write!(f, "Trust Store Writing to PEM file: {}", err)
}
}
}
}

impl Error for TrustStoreError {}

#[derive(Debug)]
pub enum NSSStore {
PEMFileCreationError(io::Error),
PEMEncodingError(ErrorStack),
WriteToFileError(io::Error),
CertUtilFailed(String),
NSSProfileNotFound,
}

impl fmt::Display for NSSStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CertUtilFailed(desc) => {
write!(f, "Certutil command failed: {}", desc)
}
Self::NSSProfileNotFound => {
write!(f, "NSS Profile Not Found")
}
Self::PEMEncodingError(err) => {
write!(f, "NSS Store PEM Encoding Failed: {}", err)
}
Self::PEMFileCreationError(err) => {
write!(f, "NSS Store PEM File creation Failed: {}", err)
}
Self::WriteToFileError(err) => {
write!(f, "NSS Store Writing to PEM file: {}", err)
}
}
}
}

impl Error for NSSStore {}
36 changes: 8 additions & 28 deletions core/src/trust_stores/linux.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use openssl::error::ErrorStack;
use openssl::x509::X509;
use std::fs;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::{fmt, fs};

use super::errors::TrustStoreError;

pub struct CAValue {
certificate: X509,
pub certificate: X509,
}

impl CAValue {
Expand All @@ -33,7 +35,8 @@ impl CAValue {
match store {
Some(store) => {
let path: String = store.get_path();
let pem_path: PathBuf = Path::new(&path).join("vanish-root.pem");
println!("Adding file to trust store: {}", path);
let pem_path: PathBuf = Path::new(&path).join("vanish-root.crt");

if let Err(err) = fs::create_dir_all(&path) {
eprintln!("Failed to create directory: {}. Error: {}", path, err);
Expand All @@ -53,7 +56,7 @@ impl CAValue {
Ok(())
}

pub fn save_cert(cert: &X509, path: &str) -> Result<(), TrustStoreError> {
fn save_cert(cert: &X509, path: &str) -> Result<(), TrustStoreError> {
let mut file: File = File::create(path)
.map_err(|err: io::Error| TrustStoreError::PEMFileCreationError(err))?;
file.write_all(
Expand Down Expand Up @@ -82,27 +85,4 @@ impl PossibleStores {
PossibleStores::Other => "/usr/share/pki/trust/anchors/".to_string(),
}
}
}

#[derive(Debug)]
pub enum TrustStoreError {
PEMFileCreationError(io::Error),
PEMEncodingError(ErrorStack),
WriteToFileError(io::Error),
}

impl fmt::Display for TrustStoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PEMEncodingError(err) => {
write!(f, "")
}
Self::PEMFileCreationError(err) => {
write!(f, "")
}
Self::WriteToFileError(err) => {
write!(f, "")
}
}
}
}
}
20 changes: 11 additions & 9 deletions core/src/trust_stores/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#[cfg(target_os = "windows")]
mod windows;

#[cfg(target_os = "macos")]
mod macos;
mod errors;
mod nss;
mod nss_profile;

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
pub use self::windows::*;
mod windows;

#[cfg(target_os = "linux")]
pub use self::linux::*;
#[cfg(target_os = "macos")]
pub use self::macos::*;
#[cfg(target_os = "windows")]
pub use self::windows::*;

#[cfg(target_os = "linux")]
pub use self::linux::*;
pub use self::nss::*;
86 changes: 86 additions & 0 deletions core/src/trust_stores/nss.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use super::nss_profile::NSSProfile;
use crate::trust_stores::errors::NSSStore;
use openssl::x509::X509;
use std::io::{self, Write};
use std::path::PathBuf;
use std::{
fs::{self, File},
path::Path,
process::Command,
};

pub struct NSSValue {
pub certificate: X509,
}

impl NSSValue {
fn get_available_profile() -> Option<NSSProfile> {
let profiles: [NSSProfile; 3] = [
NSSProfile::PkiNssdb,
NSSProfile::ChromiumNssdb,
NSSProfile::Firefox,
];

for profile in profiles {
if let Some(_path) = profile.get_valid_path() {
return Some(profile);
}
}

None
}

pub fn install_certificate(&self) -> Result<(), NSSStore> {
if let Some(profile) = NSSValue::get_available_profile() {
if let Some(path) = profile.get_valid_path() {
println!("Adding certificate to NSS profile: {}", path.display());
let pem_path: PathBuf = Path::new(&path).join("vanish-root.crt");

if let Err(err) = fs::create_dir_all(&path) {
eprintln!(
"Failed to create directory: {}. Error: {}",
path.display(),
err
);
return Err(NSSStore::PEMFileCreationError(err));
}

NSSValue::save_cert(&self.certificate, pem_path.to_str().unwrap())?;
NSSValue::run_certutil(&pem_path, &path)?;
}
} else {
eprintln!("No valid NSS profile found.");
return Err(NSSStore::NSSProfileNotFound);
}

Ok(())
}

fn save_cert(cert: &X509, path: &str) -> Result<(), NSSStore> {
let mut file: File = File::create(path).map_err(NSSStore::PEMFileCreationError)?;
file.write_all(&cert.to_pem().map_err(NSSStore::PEMEncodingError)?)
.map_err(NSSStore::WriteToFileError)?;
Ok(())
}

fn run_certutil(cert_path: &Path, profile_path: &Path) -> Result<(), NSSStore> {
let status = Command::new("certutil")
.arg("-A")
.arg("-d")
.arg(format!("sql:{}", profile_path.display()))
.arg("-t")
.arg("C,,")
.arg("-n")
.arg("vanish-root")
.arg("-i")
.arg(cert_path)
.status()
.map_err(|err: io::Error| NSSStore::CertUtilFailed(err.to_string()))?;

if !status.success() {
return Err(NSSStore::CertUtilFailed("certutil command failed".into()));
}

Ok(())
}
}
51 changes: 51 additions & 0 deletions core/src/trust_stores/nss_profile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::path::{Path, PathBuf};

pub enum NSSProfile {
PkiNssdb,
ChromiumNssdb,
Firefox,
}

impl NSSProfile {
pub fn get_valid_path(&self) -> Option<PathBuf> {
match self {
NSSProfile::PkiNssdb => {
let path: PathBuf = dirs::home_dir()?.join(".pki/nssdb");
if path.exists() {
Some(path)
} else {
None
}
}
NSSProfile::ChromiumNssdb => {
let path: PathBuf = dirs::home_dir()?.join("snap/chromium/current/.pki/nssdb");
if path.exists() {
Some(path)
} else {
None
}
}
NSSProfile::Firefox => {
let firefox_paths: [&str; 9] = [
"/usr/bin/firefox",
"/usr/bin/firefox-nightly",
"/usr/bin/firefox-developer-edition",
"/snap/firefox",
"/Applications/Firefox.app",
"/Applications/FirefoxDeveloperEdition.app",
"/Applications/Firefox Developer Edition.app",
"/Applications/Firefox Nightly.app",
"C:\\Program Files\\Mozilla Firefox",
];

for path in firefox_paths.iter() {
let profile_path: &Path = Path::new(path);
if profile_path.exists() {
return Some(profile_path.to_path_buf());
}
}
None
}
}
}
}

0 comments on commit 00fd8f3

Please sign in to comment.