rustls/
key_log_file.rs

1#[cfg(feature = "logging")]
2use crate::log::warn;
3use crate::KeyLog;
4
5use alloc::vec::Vec;
6use core::fmt::{Debug, Formatter};
7use std::env::var_os;
8use std::ffi::OsString;
9use std::fs::{File, OpenOptions};
10use std::io;
11use std::io::Write;
12use std::sync::Mutex;
13
14// Internal mutable state for KeyLogFile
15struct KeyLogFileInner {
16    file: Option<File>,
17    buf: Vec<u8>,
18}
19
20impl 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
73impl Debug for KeyLogFileInner {
74    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
75        f.debug_struct("KeyLogFileInner")
76            // Note: we omit self.buf deliberately as it may contain key data.
77            .field("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.
90pub struct KeyLogFile(Mutex<KeyLogFileInner>);
91
92impl 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 = var_os("SSLKEYLOGFILE");
97        Self(Mutex::new(KeyLogFileInner::new(var)))
98    }
99}
100
101impl 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) => {
112                warn!("error writing to key log file: {}", e);
113            }
114        }
115    }
116}
117
118impl Debug for KeyLogFile {
119    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
120        match self.0.try_lock() {
121            Ok(key_log_file) => write!(f, "{:?}", key_log_file),
122            Err(_) => write!(f, "KeyLogFile {{ <locked> }}"),
123        }
124    }
125}
126
127#[cfg(all(test, target_os = "linux"))]
128mod 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}