sidedns_core/certs/trust/
java.rs1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use super::TrustStore;
5
6const ALIAS: &str = "sidedns-local-ca";
7const STORE_PASS: &str = "changeit";
8
9pub struct JavaStore;
10
11impl TrustStore for JavaStore {
12 fn name(&self) -> &str {
13 "java"
14 }
15
16 fn is_available(&self) -> bool {
17 keytool_path().is_some() && cacerts_path().is_some()
18 }
19
20 fn is_installed(&self, _cert_path: &Path) -> bool {
21 let (Some(keytool), Some(cacerts)) = (keytool_path(), cacerts_path()) else {
22 return false;
23 };
24 let output = Command::new(keytool)
25 .args([
26 "-list",
27 "-keystore",
28 cacerts.to_str().unwrap(),
29 "-storepass",
30 STORE_PASS,
31 "-alias",
32 ALIAS,
33 ])
34 .output();
35 match output {
36 Ok(o) => o.status.success(),
37 Err(_) => false,
38 }
39 }
40
41 fn install(&self, cert_path: &Path) -> anyhow::Result<()> {
42 let keytool = keytool_path()
43 .ok_or_else(|| anyhow::anyhow!("keytool not found — is a JDK installed?"))?;
44 let cacerts =
45 cacerts_path().ok_or_else(|| anyhow::anyhow!("Java cacerts file not found"))?;
46
47 let output = Command::new(keytool)
48 .args([
49 "-importcert",
50 "-noprompt",
51 "-trustcacerts",
52 "-alias",
53 ALIAS,
54 "-keystore",
55 cacerts.to_str().unwrap(),
56 "-storepass",
57 STORE_PASS,
58 "-file",
59 cert_path.to_str().unwrap(),
60 ])
61 .output()?;
62
63 anyhow::ensure!(
64 output.status.success(),
65 "keytool -importcert failed: {}",
66 String::from_utf8_lossy(&output.stderr)
67 );
68 Ok(())
69 }
70
71 fn uninstall(&self, _cert_path: &Path) -> anyhow::Result<()> {
72 let (Some(keytool), Some(cacerts)) = (keytool_path(), cacerts_path()) else {
73 return Ok(());
74 };
75
76 if !self.is_installed(_cert_path) {
77 return Ok(());
78 }
79
80 let output = Command::new(keytool)
81 .args([
82 "-delete",
83 "-alias",
84 ALIAS,
85 "-keystore",
86 cacerts.to_str().unwrap(),
87 "-storepass",
88 STORE_PASS,
89 ])
90 .output()?;
91
92 anyhow::ensure!(
93 output.status.success(),
94 "keytool -delete failed: {}",
95 String::from_utf8_lossy(&output.stderr)
96 );
97 Ok(())
98 }
99}
100
101fn keytool_path() -> Option<PathBuf> {
102 if let Ok(java_home) = std::env::var("JAVA_HOME") {
104 let candidate = PathBuf::from(java_home).join("bin").join("keytool");
105 if candidate.exists() {
106 return Some(candidate);
107 }
108 }
109
110 std::env::var_os("PATH").as_ref().and_then(|path| {
111 std::env::split_paths(path).find_map(|dir| {
112 let c = dir.join("keytool");
113 if c.exists() {
114 return Some(c);
115 }
116 #[cfg(windows)]
117 {
118 let c = dir.join("keytool.exe");
119 if c.exists() {
120 return Some(c);
121 }
122 }
123 None
124 })
125 })
126}
127
128fn cacerts_path() -> Option<PathBuf> {
129 if let Ok(java_home) = std::env::var("JAVA_HOME") {
131 for relative in &["lib/security/cacerts", "jre/lib/security/cacerts"] {
132 let p = PathBuf::from(&java_home).join(relative);
133 if p.exists() {
134 return Some(p);
135 }
136 }
137 }
138
139 None
140}