rustls/
key_log_file.rs
1#[cfg(feature = "logging")]
2use crate::log::warn;
3use crate::KeyLog;
4use std::env;
5use std::fs::{File, OpenOptions};
6use std::io;
7use std::io::Write;
8use std::path::Path;
9use std::sync::Mutex;
10
11struct KeyLogFileInner {
13 file: Option<File>,
14 buf: Vec<u8>,
15}
16
17impl KeyLogFileInner {
18 fn new(var: Result<String, env::VarError>) -> Self {
19 let path = match var {
20 Ok(ref s) => Path::new(s),
21 Err(env::VarError::NotUnicode(ref s)) => Path::new(s),
22 Err(env::VarError::NotPresent) => {
23 return Self {
24 file: None,
25 buf: Vec::new(),
26 };
27 }
28 };
29
30 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
31 let file = match OpenOptions::new()
32 .append(true)
33 .create(true)
34 .open(path)
35 {
36 Ok(f) => Some(f),
37 Err(e) => {
38 warn!("unable to create key log file {:?}: {}", path, e);
39 None
40 }
41 };
42
43 Self {
44 file,
45 buf: Vec::new(),
46 }
47 }
48
49 fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> {
50 let mut file = match self.file {
51 None => {
52 return Ok(());
53 }
54 Some(ref f) => f,
55 };
56
57 self.buf.truncate(0);
58 write!(self.buf, "{} ", label)?;
59 for b in client_random.iter() {
60 write!(self.buf, "{:02x}", b)?;
61 }
62 write!(self.buf, " ")?;
63 for b in secret.iter() {
64 write!(self.buf, "{:02x}", b)?;
65 }
66 writeln!(self.buf)?;
67 file.write_all(&self.buf)
68 }
69}
70
71pub struct KeyLogFile(Mutex<KeyLogFileInner>);
80
81impl KeyLogFile {
82 pub fn new() -> Self {
85 let var = env::var("SSLKEYLOGFILE");
86 Self(Mutex::new(KeyLogFileInner::new(var)))
87 }
88}
89
90impl KeyLog for KeyLogFile {
91 fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
92 #[cfg_attr(not(feature = "logging"), allow(unused_variables))]
93 match self
94 .0
95 .lock()
96 .unwrap()
97 .try_write(label, client_random, secret)
98 {
99 Ok(()) => {}
100 Err(e) => {
101 warn!("error writing to key log file: {}", e);
102 }
103 }
104 }
105}
106
107#[cfg(all(test, target_os = "linux"))]
108mod test {
109 use super::*;
110
111 fn init() {
112 let _ = env_logger::builder()
113 .is_test(true)
114 .try_init();
115 }
116
117 #[test]
118 fn test_env_var_is_not_unicode() {
119 init();
120 let mut inner = KeyLogFileInner::new(Err(env::VarError::NotUnicode(
121 "/tmp/keylogfileinnertest".into(),
122 )));
123 assert!(inner
124 .try_write("label", b"random", b"secret")
125 .is_ok());
126 }
127
128 #[test]
129 fn test_env_var_is_not_set() {
130 init();
131 let mut inner = KeyLogFileInner::new(Err(env::VarError::NotPresent));
132 assert!(inner
133 .try_write("label", b"random", b"secret")
134 .is_ok());
135 }
136
137 #[test]
138 fn test_env_var_cannot_be_opened() {
139 init();
140 let mut inner = KeyLogFileInner::new(Ok("/dev/does-not-exist".into()));
141 assert!(inner
142 .try_write("label", b"random", b"secret")
143 .is_ok());
144 }
145
146 #[test]
147 fn test_env_var_cannot_be_written() {
148 init();
149 let mut inner = KeyLogFileInner::new(Ok("/dev/full".into()));
150 assert!(inner
151 .try_write("label", b"random", b"secret")
152 .is_err());
153 }
154}