You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
7.9 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. use crate::hooks::{self, FileStorageHookData, Hook, HookEnvData, HookType};
  2. use crate::logs::HasLogger;
  3. use crate::template::render_template;
  4. use acme_common::b64_encode;
  5. use acme_common::crypto::{KeyPair, X509Certificate};
  6. use acme_common::error::Error;
  7. use serde::Serialize;
  8. use std::collections::HashMap;
  9. use std::fmt;
  10. use std::fs::{File, OpenOptions};
  11. use std::io::{Read, Write};
  12. use std::path::{Path, PathBuf};
  13. #[cfg(target_family = "unix")]
  14. use std::os::unix::fs::OpenOptionsExt;
  15. #[derive(Clone, Debug)]
  16. pub struct FileManager {
  17. pub account_name: String,
  18. pub account_directory: String,
  19. pub crt_name: String,
  20. pub crt_name_format: String,
  21. pub crt_directory: String,
  22. pub crt_key_type: String,
  23. pub cert_file_mode: u32,
  24. pub cert_file_owner: Option<String>,
  25. pub cert_file_group: Option<String>,
  26. pub pk_file_mode: u32,
  27. pub pk_file_owner: Option<String>,
  28. pub pk_file_group: Option<String>,
  29. pub hooks: Vec<Hook>,
  30. pub env: HashMap<String, String>,
  31. }
  32. impl HasLogger for FileManager {
  33. fn warn(&self, msg: &str) {
  34. log::warn!("{}: {}", self, msg);
  35. }
  36. fn info(&self, msg: &str) {
  37. log::info!("{}: {}", self, msg);
  38. }
  39. fn debug(&self, msg: &str) {
  40. log::debug!("{}: {}", self, msg);
  41. }
  42. fn trace(&self, msg: &str) {
  43. log::trace!("{}: {}", self, msg);
  44. }
  45. }
  46. impl fmt::Display for FileManager {
  47. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  48. let s = if !self.crt_name.is_empty() {
  49. format!("certificate \"{}_{}\"", self.crt_name, self.crt_key_type)
  50. } else {
  51. format!("account \"{}\"", self.account_name)
  52. };
  53. write!(f, "{}", s)
  54. }
  55. }
  56. #[derive(Clone)]
  57. enum FileType {
  58. Account,
  59. PrivateKey,
  60. Certificate,
  61. }
  62. impl fmt::Display for FileType {
  63. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  64. let s = match self {
  65. FileType::Account => "account",
  66. FileType::PrivateKey => "pk",
  67. FileType::Certificate => "crt",
  68. };
  69. write!(f, "{}", s)
  70. }
  71. }
  72. #[derive(Clone, Serialize)]
  73. pub struct CertFileFormat {
  74. pub ext: String,
  75. pub file_type: String,
  76. pub key_type: String,
  77. pub name: String,
  78. }
  79. fn get_file_full_path(
  80. fm: &FileManager,
  81. file_type: FileType,
  82. ) -> Result<(String, String, PathBuf), Error> {
  83. let base_path = match file_type {
  84. FileType::Account => &fm.account_directory,
  85. FileType::PrivateKey => &fm.crt_directory,
  86. FileType::Certificate => &fm.crt_directory,
  87. };
  88. let file_name = match file_type {
  89. FileType::Account => format!(
  90. "{account}.{file_type}.{ext}",
  91. account = b64_encode(&fm.account_name),
  92. file_type = file_type,
  93. ext = "bin"
  94. ),
  95. FileType::PrivateKey | FileType::Certificate => {
  96. let fmt_data = CertFileFormat {
  97. key_type: fm.crt_key_type.to_string(),
  98. ext: "pem".into(),
  99. file_type: file_type.to_string(),
  100. name: fm.crt_name.to_owned(),
  101. };
  102. render_template(&fm.crt_name_format, &fmt_data)?
  103. }
  104. };
  105. let mut path = PathBuf::from(&base_path);
  106. path.push(&file_name);
  107. Ok((base_path.to_string(), file_name, path))
  108. }
  109. fn get_file_path(fm: &FileManager, file_type: FileType) -> Result<PathBuf, Error> {
  110. let (_, _, path) = get_file_full_path(fm, file_type)?;
  111. Ok(path)
  112. }
  113. fn read_file(fm: &FileManager, path: &Path) -> Result<Vec<u8>, Error> {
  114. fm.trace(&format!("reading file {:?}", path));
  115. let mut file =
  116. File::open(path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?;
  117. let mut contents = vec![];
  118. file.read_to_end(&mut contents)?;
  119. Ok(contents)
  120. }
  121. #[cfg(unix)]
  122. fn set_owner(fm: &FileManager, path: &Path, file_type: FileType) -> Result<(), Error> {
  123. let (uid, gid) = match file_type {
  124. FileType::Certificate => (fm.cert_file_owner.to_owned(), fm.cert_file_group.to_owned()),
  125. FileType::PrivateKey => (fm.pk_file_owner.to_owned(), fm.pk_file_group.to_owned()),
  126. FileType::Account => {
  127. // The account file does not need to be accessible to users other different from the current one.
  128. return Ok(());
  129. }
  130. };
  131. let uid = match uid {
  132. Some(u) => {
  133. if u.bytes().all(|b| b.is_ascii_digit()) {
  134. let raw_uid = u
  135. .parse::<u32>()
  136. .map_err(|_| Error::from("unable to parse the UID"))?;
  137. let nix_uid = nix::unistd::Uid::from_raw(raw_uid);
  138. Some(nix_uid)
  139. } else {
  140. let user = nix::unistd::User::from_name(&u)?;
  141. user.map(|u| u.uid)
  142. }
  143. }
  144. None => None,
  145. };
  146. let gid = match gid {
  147. Some(g) => {
  148. if g.bytes().all(|b| b.is_ascii_digit()) {
  149. let raw_gid = g
  150. .parse::<u32>()
  151. .map_err(|_| Error::from("unable to parse the GID"))?;
  152. let nix_gid = nix::unistd::Gid::from_raw(raw_gid);
  153. Some(nix_gid)
  154. } else {
  155. let grp = nix::unistd::Group::from_name(&g)?;
  156. grp.map(|g| g.gid)
  157. }
  158. }
  159. None => None,
  160. };
  161. match uid {
  162. Some(u) => fm.trace(&format!("{:?}: setting the uid to {}", path, u.as_raw())),
  163. None => fm.trace(&format!("{:?}: uid unchanged", path)),
  164. };
  165. match gid {
  166. Some(g) => fm.trace(&format!("{:?}: setting the gid to {}", path, g.as_raw())),
  167. None => fm.trace(&format!("{:?}: gid unchanged", path)),
  168. };
  169. match nix::unistd::chown(path, uid, gid) {
  170. Ok(_) => Ok(()),
  171. Err(e) => Err(format!("{}", e).into()),
  172. }
  173. }
  174. fn write_file(fm: &FileManager, file_type: FileType, data: &[u8]) -> Result<(), Error> {
  175. let (file_directory, file_name, path) = get_file_full_path(fm, file_type.clone())?;
  176. let mut hook_data = FileStorageHookData {
  177. file_name,
  178. file_directory,
  179. file_path: path.to_owned(),
  180. env: HashMap::new(),
  181. };
  182. hook_data.set_env(&fm.env);
  183. let is_new = !path.is_file();
  184. if is_new {
  185. hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreCreate)?;
  186. } else {
  187. hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePreEdit)?;
  188. }
  189. fm.trace(&format!("writing file {:?}", path));
  190. let mut file = if cfg!(unix) {
  191. let mut options = OpenOptions::new();
  192. options.mode(match &file_type {
  193. FileType::Certificate => fm.cert_file_mode,
  194. FileType::PrivateKey => fm.pk_file_mode,
  195. FileType::Account => crate::DEFAULT_ACCOUNT_FILE_MODE,
  196. });
  197. options
  198. .write(true)
  199. .create(true)
  200. .open(&path)
  201. .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?
  202. } else {
  203. File::create(&path).map_err(|e| Error::from(e).prefix(&path.display().to_string()))?
  204. };
  205. file.write_all(data)
  206. .map_err(|e| Error::from(e).prefix(&path.display().to_string()))?;
  207. if cfg!(unix) {
  208. set_owner(fm, &path, file_type).map_err(|e| e.prefix(&path.display().to_string()))?;
  209. }
  210. if is_new {
  211. hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostCreate)?;
  212. } else {
  213. hooks::call(fm, &fm.hooks, &hook_data, HookType::FilePostEdit)?;
  214. }
  215. Ok(())
  216. }
  217. pub fn get_account_data(fm: &FileManager) -> Result<Vec<u8>, Error> {
  218. let path = get_file_path(fm, FileType::Account)?;
  219. read_file(fm, &path)
  220. }
  221. pub fn set_account_data(fm: &FileManager, data: &[u8]) -> Result<(), Error> {
  222. write_file(fm, FileType::Account, data)
  223. }
  224. pub fn get_keypair(fm: &FileManager) -> Result<KeyPair, Error> {
  225. let path = get_file_path(fm, FileType::PrivateKey)?;
  226. let raw_key = read_file(fm, &path)?;
  227. let key = KeyPair::from_pem(&raw_key)?;
  228. Ok(key)
  229. }
  230. pub fn set_keypair(fm: &FileManager, key_pair: &KeyPair) -> Result<(), Error> {
  231. let data = key_pair.private_key_to_pem()?;
  232. write_file(fm, FileType::PrivateKey, &data)
  233. }
  234. pub fn get_certificate(fm: &FileManager) -> Result<X509Certificate, Error> {
  235. let path = get_file_path(fm, FileType::Certificate)?;
  236. let raw_crt = read_file(fm, &path)?;
  237. let crt = X509Certificate::from_pem(&raw_crt)?;
  238. Ok(crt)
  239. }
  240. pub fn write_certificate(fm: &FileManager, data: &[u8]) -> Result<(), Error> {
  241. write_file(fm, FileType::Certificate, data)
  242. }
  243. fn check_files(fm: &FileManager, file_types: &[FileType]) -> bool {
  244. for t in file_types.iter().cloned() {
  245. let path = match get_file_path(fm, t) {
  246. Ok(p) => p,
  247. Err(_) => {
  248. return false;
  249. }
  250. };
  251. fm.trace(&format!(
  252. "testing file path: {}",
  253. path.to_str().unwrap_or_default()
  254. ));
  255. if !path.is_file() {
  256. return false;
  257. }
  258. }
  259. true
  260. }
  261. pub fn account_files_exists(fm: &FileManager) -> bool {
  262. let file_types = vec![FileType::Account];
  263. check_files(fm, &file_types)
  264. }
  265. pub fn certificate_files_exists(fm: &FileManager) -> bool {
  266. let file_types = vec![FileType::PrivateKey, FileType::Certificate];
  267. check_files(fm, &file_types)
  268. }