| 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 | |