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