zebra_rpc/server/
cookie.rs1use base64::{engine::general_purpose::STANDARD, Engine as _};
4use color_eyre::Result;
5use rand::RngCore;
6use subtle::ConstantTimeEq;
7
8use std::{
9 fs::{remove_file, File},
10 io::Write,
11 path::Path,
12};
13
14#[cfg(unix)]
15use std::os::unix::fs::OpenOptionsExt;
16
17const FILE: &str = ".cookie";
19
20#[derive(Clone, Debug)]
22pub struct Cookie(String);
23
24impl Cookie {
25 pub fn authenticate(&self, passwd: String) -> bool {
27 if passwd.len() != self.0.len() {
28 return false;
29 }
30 passwd.as_bytes().ct_eq(self.0.as_bytes()).into()
31 }
32}
33
34impl Default for Cookie {
35 fn default() -> Self {
36 let mut bytes = [0u8; 32];
37 rand::thread_rng().fill_bytes(&mut bytes);
38
39 Self(STANDARD.encode(bytes))
40 }
41}
42
43pub fn write_to_disk(cookie: &Cookie, dir: &Path) -> Result<()> {
48 std::fs::create_dir_all(dir)?;
49
50 let cookie_path = dir.join(FILE);
51
52 if cookie_path
53 .symlink_metadata()
54 .map(|m| m.file_type().is_symlink())
55 .unwrap_or(false)
56 {
57 return Err(color_eyre::eyre::eyre!(
58 "cookie path {cookie_path:?} is a symlink, refusing to write"
59 ));
60 }
61
62 let mut file = create_owner_only_file(&cookie_path)?;
63 file.write_all(format!("__cookie__:{}", cookie.0).as_bytes())?;
64
65 tracing::info!("RPC auth cookie written to disk");
66
67 Ok(())
68}
69
70fn create_owner_only_file(path: &Path) -> Result<File> {
76 let mut opts = std::fs::OpenOptions::new();
77 opts.write(true).create(true).truncate(true);
78
79 #[cfg(unix)]
80 opts.mode(0o600);
81
82 Ok(opts.open(path)?)
83}
84
85pub fn remove_from_disk(dir: &Path) -> Result<()> {
87 remove_file(dir.join(FILE))?;
88
89 tracing::info!("RPC auth cookie removed from disk");
90
91 Ok(())
92}