1 | use alloc::vec::Vec; |
2 | use core::fmt::{Debug, Formatter}; |
3 | use std::env::var_os; |
4 | use std::ffi::OsString; |
5 | use std::fs::{File, OpenOptions}; |
6 | use std::io; |
7 | use std::io::Write; |
8 | use std::sync::Mutex; |
9 | |
10 | use crate::KeyLog; |
11 | use crate::log::warn; |
12 | |
13 | // Internal mutable state for KeyLogFile |
14 | struct KeyLogFileInner { |
15 | file: Option<File>, |
16 | buf: Vec<u8>, |
17 | } |
18 | |
19 | impl KeyLogFileInner { |
20 | fn new(var: Option<OsString>) -> Self { |
21 | let Some(path) = &var else { |
22 | return Self { |
23 | file: None, |
24 | buf: Vec::new(), |
25 | }; |
26 | }; |
27 | |
28 | #[cfg_attr (not(feature = "logging" ), allow(unused_variables))] |
29 | let file = match OpenOptions::new() |
30 | .append(true) |
31 | .create(true) |
32 | .open(path) |
33 | { |
34 | Ok(f) => Some(f), |
35 | Err(e) => { |
36 | warn!("unable to create key log file {:?}: {}" , path, e); |
37 | None |
38 | } |
39 | }; |
40 | |
41 | Self { |
42 | file, |
43 | buf: Vec::new(), |
44 | } |
45 | } |
46 | |
47 | fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> { |
48 | let file = match &mut self.file { |
49 | None => { |
50 | return Ok(()); |
51 | } |
52 | Some(f) => f, |
53 | }; |
54 | |
55 | self.buf.truncate(0); |
56 | write!(self.buf, " {} " , label)?; |
57 | for b in client_random.iter() { |
58 | write!(self.buf, " {:02x}" , b)?; |
59 | } |
60 | write!(self.buf, " " )?; |
61 | for b in secret.iter() { |
62 | write!(self.buf, " {:02x}" , b)?; |
63 | } |
64 | writeln!(self.buf)?; |
65 | file.write_all(&self.buf) |
66 | } |
67 | } |
68 | |
69 | impl Debug for KeyLogFileInner { |
70 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
71 | f&mut DebugStruct<'_, '_>.debug_struct("KeyLogFileInner" ) |
72 | // Note: we omit self.buf deliberately as it may contain key data. |
73 | .field(name:"file" , &self.file) |
74 | .finish() |
75 | } |
76 | } |
77 | |
78 | /// [`KeyLog`] implementation that opens a file whose name is |
79 | /// given by the `SSLKEYLOGFILE` environment variable, and writes |
80 | /// keys into it. |
81 | /// |
82 | /// If `SSLKEYLOGFILE` is not set, this does nothing. |
83 | /// |
84 | /// If such a file cannot be opened, or cannot be written then |
85 | /// this does nothing but logs errors at warning-level. |
86 | pub struct KeyLogFile(Mutex<KeyLogFileInner>); |
87 | |
88 | impl KeyLogFile { |
89 | /// Makes a new `KeyLogFile`. The environment variable is |
90 | /// inspected and the named file is opened during this call. |
91 | pub fn new() -> Self { |
92 | let var: Option = var_os(key:"SSLKEYLOGFILE" ); |
93 | Self(Mutex::new(KeyLogFileInner::new(var))) |
94 | } |
95 | } |
96 | |
97 | impl KeyLog for KeyLogFile { |
98 | fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) { |
99 | #[cfg_attr (not(feature = "logging" ), allow(unused_variables))] |
100 | match self |
101 | .0 |
102 | .lock() |
103 | .unwrap() |
104 | .try_write(label, client_random, secret) |
105 | { |
106 | Ok(()) => {} |
107 | Err(e: Error) => { |
108 | warn!("error writing to key log file: {}" , e); |
109 | } |
110 | } |
111 | } |
112 | } |
113 | |
114 | impl Debug for KeyLogFile { |
115 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
116 | match self.0.try_lock() { |
117 | Ok(key_log_file: MutexGuard<'_, KeyLogFileInner>) => write!(f, " {:?}" , key_log_file), |
118 | Err(_) => write!(f, "KeyLogFile {{ <locked> }}" ), |
119 | } |
120 | } |
121 | } |
122 | |
123 | #[cfg (all(test, target_os = "linux" ))] |
124 | mod tests { |
125 | use super::*; |
126 | |
127 | fn init() { |
128 | let _ = env_logger::builder() |
129 | .is_test(true) |
130 | .try_init(); |
131 | } |
132 | |
133 | #[test ] |
134 | fn test_env_var_is_not_set() { |
135 | init(); |
136 | let mut inner = KeyLogFileInner::new(None); |
137 | assert!( |
138 | inner |
139 | .try_write("label" , b"random" , b"secret" ) |
140 | .is_ok() |
141 | ); |
142 | } |
143 | |
144 | #[test ] |
145 | fn test_env_var_cannot_be_opened() { |
146 | init(); |
147 | let mut inner = KeyLogFileInner::new(Some("/dev/does-not-exist" .into())); |
148 | assert!( |
149 | inner |
150 | .try_write("label" , b"random" , b"secret" ) |
151 | .is_ok() |
152 | ); |
153 | } |
154 | |
155 | #[test ] |
156 | fn test_env_var_cannot_be_written() { |
157 | init(); |
158 | let mut inner = KeyLogFileInner::new(Some("/dev/full" .into())); |
159 | assert!( |
160 | inner |
161 | .try_write("label" , b"random" , b"secret" ) |
162 | .is_err() |
163 | ); |
164 | } |
165 | } |
166 | |