sgxs-tools 0.9.4

Utilities for working with the SGX stream format.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
use super::*;

use std::borrow::Cow;
use std::fmt::{self, Write as FmtWrite};
use std::io::{self, stdout, Write};

use yansi::{Color, Paint};

use crate::{FailTrace, paintalt};

impl DebugSupport for SgxCpuSupport {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        writeln!(out, "It appears your hardware does not have SGX support.")?;
        out.verbose();
        if let Some(SgxCpuSupportInner { sgx: Err(ref e) }) = self.inner {
            writeln!(out, "CPUID error: {}", FailTrace(&e))?;
        } else {
            writeln!(out, "CPUID.(EAX=07H,ECX=0H):EBX.SGX[bit 2] = 0")?;
        }
        out.help_link("cpu-support")
    }
}

impl DebugSupport for SgxCpuConfiguration {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        let mut debug_msr = true;
        let help_link;
        
        let inner = self.inner.as_ref().unwrap();

        if inner.sgx1 && !inner.enclave_size_ok {
            help_link = "invalid-cpuid";

            writeln!(out, "Your hardware supports SGX, but the maximum enclave size is misconfigured. This could indicate a CPU or hypervisor bug.")?;

            debug_msr = false;
        } else {
            help_link = "cpu-configuration";
            // try to say something useful about BIOS configuration
            fn is_efi_perm_error(err: &Error) -> bool {
                if let Some(DetectError::EfiVariableError(ioerr)) = err.downcast_ref() {
                    ioerr.kind() == io::ErrorKind::PermissionDenied
                } else {
                    false
                }
            }

            let mut swcontrol_msg = Cow::from("Reboot your machine and try to change the SGX setting from “software controlled” to “enabled”. Alternatively, try updating your BIOS to the latest version or contact your BIOS vendor.");
            if !Paint::is_enabled() {
                swcontrol_msg = swcontrol_msg.replace('', r#"""#).replace('', r#"""#).into();
            }
            const SGX_DISABLED: EfiSoftwareguardstatus = EfiSoftwareguardstatus { status: SgxEnableStatus::Disabled };
            const SGX_ENABLED: EfiSoftwareguardstatus = EfiSoftwareguardstatus { status: SgxEnableStatus::Enabled };
            const SGX_SWCONTROL: EfiSoftwareguardstatus = EfiSoftwareguardstatus { status: SgxEnableStatus::SoftwareControlled };
            const SGX_UNKNOWN: EfiSoftwareguardstatus = EfiSoftwareguardstatus { status: SgxEnableStatus::Unknown };

            writeln!(out, "Your hardware supports SGX, but it's not enabled.\n")?;

            match (&inner.efi_status, &inner.efi_epcbios) {
                (&Ok(SGX_DISABLED), _) => {
                    writeln!(out, "Your BIOS supports SGX, but it's disabled. Reboot your machine and enable SGX in your BIOS.")?;
                    debug_msr = false;
                },
                (&Ok(SGX_ENABLED), _) => {
                    writeln!(out, "Your BIOS says it supports SGX and SGX is enabled, but it's not. Try updating your BIOS to the latest version or contact your BIOS vendor.")?;
                },
                (&Ok(SGX_SWCONTROL), Err(e)) if is_efi_perm_error(&e) => {
                    writeln!(out, "Your BIOS says it supports SGX reconfiguration, but the control mechanism could not be accessed due to a permission issue.")?;
                    writeln!(out, "\nWould you like to re-run this program with sudo to try again?\n{}", Paint::red("(not supported yet)"))?; //TODO
                }
                (&Ok(SGX_SWCONTROL), Err(_)) => {
                    write!(out, "Your BIOS says it supports SGX reconfiguration, but there is a problem with the control mechanism.")?;
                    writeln!(out, "{}", swcontrol_msg)?;
                },
                (_, Ok(epcbios)) => {
                    if epcbios.current_epc_size > 0 {
                        write!(out, "Your BIOS says it supports SGX and SGX is enabled, but it's not.")?;
                        writeln!(out, "{}", swcontrol_msg)?;
                    } else if epcbios.max_epc_size > 0 {
                        match inner.efi_epcsw {
                            Ok(EfiEpcsw { epc_size: 0 }) | Err(_) => {
                                writeln!(out, "Your BIOS supports SGX but it's currently disabled.\n\nWould you like to enable SGX automatically after the next reboot?\n{}", Paint::red("(not supported yet)"))?; //TODO
                            },
                            Ok(_) => {
                                writeln!(out, "Your BIOS supports SGX but it's currently disabled. However, it's configured to enabled SGX automatically after the next reboot.")?;
                            }
                        }
                        debug_msr = false;
                    } else {
                        write!(out, "Your BIOS says it supports SGX reconfiguration, but there is a problem with the control mechanism.")?;
                        writeln!(out, "{}", swcontrol_msg)?;
                    }
                },
                (Err(e), _) if is_efi_perm_error(&e) => {
                    writeln!(out, "BIOS support for SGX could not be determined due to a permission issue.")?;
                    writeln!(out, "\nWould you like to re-run this program with sudo to try again?\n{}", Paint::red("TODO"))?;
                }
                (&Ok(SGX_UNKNOWN), Err(_)) | (Err(_), Err(_)) => {
                    writeln!(out, "BIOS support for SGX could not be determined. Reboot your machine and try to enable SGX in your BIOS. Alternatively, try updating your BIOS to the latest version or contact your BIOS vendor.")?;
                }
            }
        }

        out.verbose();
        if let Some(ref cpuid_err) = inner.cpuid_err {
            let s = cpuid_err.to_string();
            if s.starts_with("CPUID") {
                writeln!(out, "{}", FailTrace(&cpuid_err))?;
            } else {
                writeln!(out, "CPUID error: {}", FailTrace(&cpuid_err))?;
            }
        }
        match &inner.msr_3ah {
            Ok(Msr3ah { sgx: true, locked: true, .. }) => {},
            Ok(Msr3ah { sgx, locked, .. }) => {
                writeln!(out, "MSR 3Ah IA32_FEATURE_CONTROL.LOCK = {}", *locked as u8)?;
                writeln!(out, "MSR 3Ah IA32_FEATURE_CONTROL.SGX_ENABLE = {}", *sgx as u8)?;
            },
            Err(e) => {
                writeln!(out, "Error reading MSR 3Ah: {}", FailTrace(e))?;
                if debug_msr {
                    writeln!(out, "More debugging information might be available by re-running this program with sudo. Would you like to do that?\n{}", Paint::red("(not supported yet)"))?; //TODO
                }
            }
        }
        match &inner.efi_epcbios {
            Ok(var) => writeln!(out, "{:?}", var)?,
            Err(e) => writeln!(out, "Error reading EFI variable EPCBIOS: {}", FailTrace(e))?,
        }
        match &inner.efi_epcsw {
            Ok(var) => writeln!(out, "{:?}", var)?,
            Err(e) => writeln!(out, "Error reading EFI variable EPCSW: {}", FailTrace(e))?,
        }
        match &inner.efi_status {
            Ok(var) => writeln!(out, "{:?}", var)?,
            Err(e) => writeln!(out, "Error reading EFI variable SOFTWAREGUARDSTATUS: {}", FailTrace(e))?,
        }

        out.help_link(help_link)
    }
}

impl DebugSupport for EnclaveAttributes {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        writeln!(out, "Your hardware supports SGX, but the supported SGX attributes are misconfigured. This could indicate a CPU or hypervisor bug.")?;
        out.verbose();
        match self.inner {
            Some(EnclaveAttributesInner { cpuid_12h_1: Err(ref e), .. }) => writeln!(out, "CPUID error: {}", FailTrace(e))?,
            Some(EnclaveAttributesInner { cpuid_12h_1: Ok(attr), .. }) => {
                writeln!(out, "Supported attributes: {:?}", attr.attributes_flags_valid)?;
                writeln!(out, "Supported XFRM: {:016x}", attr.attributes_xfrm_valid)?;
            },
            _ => unreachable!(),
        }
        out.help_link("invalid-cpuid")
    }
}

impl DebugSupport for EnclavePageCache {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        let inner = self.inner.as_ref().unwrap();
        if !inner.any_unknown && inner.cpuid_12h_epc.is_ok() {
            writeln!(out, "Your hardware supports SGX, but no Enclave Page Cache (EPC) is configured. This could indicate a BIOS or hypervisor bug.")?;
        } else {
            writeln!(out, "Your hardware supports SGX, but the Enclave Page Cache (EPC) is misconfigured. This could indicate a CPU or hypervisor bug.")?;
        }

        out.verbose();
        match &inner.cpuid_12h_epc {
            Err(e) => writeln!(out, "CPUID error: {}", FailTrace(e))?,
            Ok(sections) => {
                for (i, section) in sections.iter().enumerate() {
                    match section {
                        Cpuid12hEnum::Epc { ty: EpcType::ConfidentialityIntegrityProtected, .. } => {},
                        Cpuid12hEnum::Epc { ty, .. } => writeln!(out, "CPUID.(EAX=12H,ECX={:X}H):ECX[3:0] = {:?}", i + 2, ty)?,
                        sect => writeln!(out, "CPUID.(EAX=12H,ECX={:X}H):EAX[3:0] = {:?}", i + 2, sect)?,
                    }
                }
            },
        }
        out.help_link("invalid-cpuid")
    }
}

impl DebugSupport for FlcCpuConfiguration {
    fn debug(&self, mut out: debug::Output, items: &DetectItemMap) -> fmt::Result {
        // don't print any guidance for FLC unless SGX itself is enabled
        if items.lookup::<Isa>().supported() != Status::Supported {
            return Ok(());
        }

        if let Some(Err(err)) = &self.msr_3ah {
            // don't print any guidance if it appears to work
            if items.lookup::<RunEnclaveProdWrap>().supported() == Status::Supported {
                return Ok(());
            }

            writeln!(out, "Your hardware supports Flexible Launch Control, but whether it's enabled could not be determined. More information might be available by re-running this program with sudo. Would you like to do that?\n{}", Paint::red("(not supported yet)"))?; //TODO

            out.verbose();
            writeln!(out, "Error reading MSR 3Ah: {}", FailTrace(err))?;
        } else {
            writeln!(out, "Your hardware supports Flexible Launch Control, but it's not enabled in the BIOS. Reboot your machine and try to enable FLC in your BIOS. Alternatively, try updating your BIOS to the latest version or contact your BIOS vendor.")?;

            out.verbose();
            writeln!(out, "MSR 3Ah IA32_FEATURE_CONTROL.SGX_LC = 0")?;
        }

        out.help_link("flc-cpu-configuration")
    }
}

impl DebugSupport for DeviceLoader {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        fn len_to_mod_str(v: &[String]) -> (&str, &str) {
            match v.len() {
                1 => ("", "is"),
                _ => ("s", "are"),
            }
        }

        let inner = self.inner.as_ref().unwrap();

        let err = inner.devpath.as_ref().unwrap_err();

        if let Some(DetectError::NotAvailableInTest) = err.downcast_ref() {
            return Ok(());
        }

        let mut known_error = false;
        if let Some(ioe) = err.downcast_ref::<io::Error>() {
            if ioe.kind() == io::ErrorKind::NotFound {
                known_error = true;
                writeln!(out, "The SGX device (/dev/sgx/enclave, /dev/sgx or /dev/isgx) is not present.\n")?;

                match inner.modstatus {
                    Ok(KmodStatus { ref loaded, .. }) if !loaded.is_empty() => {
                        let (suffix, verb) = len_to_mod_str(&loaded);
                        writeln!(out, "The following module{} {} loaded: {}. Check your kernel logs to see why the device is not available.", suffix, verb, loaded.join(", "))?;
                    },
                    Ok(KmodStatus { ref available, .. }) if !available.is_empty() => {
                        let (suffix, verb) = len_to_mod_str(&available);
                        writeln!(out, "The following module{} {} available, but not loaded: {}. Would you like to load the module{} now? (N.B. you might want to configure the module to load automatically.)\n{}", suffix, verb, available.join(", "), suffix, Paint::red("(not supported yet)"))?; //TODO
                    },
                    Ok(_) => {
                        writeln!(out, "It appears you don't have the kernel module installed. Would you like to install it now?\n{}", Paint::red("(not supported yet)"))?; //TODO
                    },
                    Err(_) => {
                        writeln!(out, "It could not be detected whether you have an SGX driver installed. Please make sure the SGX driver is installed and loaded correctly.")?;
                    },
                }
            } else if ioe.kind() == io::ErrorKind::PermissionDenied {
                known_error = true;
                writeln!(out, "Permission denied while opening the SGX device (/dev/sgx/enclave, /dev/sgx or /dev/isgx). Make sure you have the necessary permissions to create SGX enclaves. If you are running in a container, make sure the device permissions are correctly set on the container.")?;
            }
        }

        if !known_error {
            writeln!(out, "The SGX device (/dev/sgx/enclave, /dev/sgx or /dev/isgx) could not be opened: {}.", err)?;
        }

        out.verbose();
        writeln!(out, "Error opening device: {}", FailTrace(err))?;
        if let Err(ref e) = inner.modstatus {
            writeln!(out, "Error checking module status: {}", FailTrace(e))?;
        }

        out.help_link("sgx-driver")
    }
}

impl DebugSupport for AesmService {
    fn debug(&self, mut out: debug::Output, items: &DetectItemMap) -> fmt::Result {
        let inner = self.inner.as_ref().unwrap();

        if let Err(ref e) = inner.service {
            if let Some(DetectError::NotAvailableInTest) = e.downcast_ref() {
                return Ok(());
            }
        }

        let reason = if items.lookup::<RunEnclave>().supported() == Status::Supported {
            "AESM is needed for generating EPID attestations."
        } else {
            "AESM is needed for launching enclaves and generating attestations."
        };

        writeln!(out, "AESM could not be contacted. {}\n", reason)?;

        match inner.install_state {
            Some(AesmStatus::Absent) => writeln!(out, "AESM appears to not be installed. Would you like to install it?\n{}", Paint::red("(not supported yet)"))?, //TODO
            Some(AesmStatus::Installed) => writeln!(out, "AESM appears to be installed, but it's not running. Please check your AESM installation.")?,
            Some(AesmStatus::Running) => writeln!(out, "AESM appears to be running. Please file a bug report at https://github.com/fortanix/rust-sgx or contact your Fortanix representative.")?,
            None => writeln!(out, "Please check your AESM installation.")?
        }

        out.verbose();
        writeln!(out, "{}", FailTrace(inner.service.as_ref().unwrap_err()))?;

        out.help_link("aesm-service")
    }
}

impl DebugSupport for RunEnclaveDebug {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        writeln!(out, "The enclave could not be launched.")?;

        out.verbose();
        if let Some(Err(ref e)) = self.success {
            writeln!(out, "{}", FailTrace(e))?;
        }

        out.help_link("run-enclave-debug")
    }
}

impl DebugSupport for RunEnclaveProd {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        writeln!(out, "The enclave could not be launched. This might indicate a problem with FLC.")?;

        out.verbose();
        if let Some(Err(ref e)) = self.success {
            writeln!(out, "{}", FailTrace(e))?;
        }

        out.help_link("run-enclave-prod")
    }
}

impl DebugSupport for RunEnclaveProdWl {
    fn debug(&self, mut out: debug::Output, _items: &DetectItemMap) -> fmt::Result {
        writeln!(out, "The enclave could not be launched. This might indicate a problem with AESM or your internet connection.")?;

        out.verbose();
        if let Some(Err(ref e)) = self.success {
            writeln!(out, "{}", FailTrace(e))?;
        }

        out.help_link("run-enclave-prodwl")
    }
}

#[derive(Copy, Clone)]
enum DebugState {
    Off,
    DebugStart,
    LineStart,
    InLine,
    Hidden,
}

pub struct Output {
    name: Vec<&'static str>,
    verbose: bool,
    started_output: bool,
    debug_state: DebugState,
}

impl Output {
    pub fn new(path: Vec<&'static str>, verbose: bool) -> Self {
        Output {
            name: path,
            verbose,
            started_output: false,
            debug_state: DebugState::Off,
        }
    }

    pub fn verbose(&mut self) {
        if let DebugState::Off = self.debug_state {
            self.debug_state = DebugState::DebugStart
        }
    }

    pub fn help_link(mut self, name: &str) -> fmt::Result {
        self.debug_state = DebugState::Off;
        writeln!(self, "\nMore information: https://edp.fortanix.com/docs/installation/help/#{}", name)
    }
}

impl fmt::Write for Output {
    fn write_str(&mut self, mut s: &str) -> fmt::Result {
        let fmt = |_| fmt::Error;
        
        let stdout = stdout();
        let mut stdout = stdout.lock();
        if !self.started_output {
            self.started_output = true;
            let path = self.name.join(" > ");
            writeln!(stdout, "\n{}{}", paintalt("🕮  ", "help: ").fg(Color::Blue), Paint::new(path).bold()).map_err(fmt)?;
        }

        while !s.is_empty() {
            if let DebugState::DebugStart = self.debug_state {
                writeln!(stdout, "").map_err(fmt)?;
                self.debug_state = DebugState::LineStart;
            }

            if let DebugState::LineStart = self.debug_state {
                self.debug_state = if !self.verbose {
                    writeln!(stdout, "{}", Paint::white("(run with `--verbose` for more details)")).map_err(fmt)?;
                    DebugState::Hidden
                } else {
                    write!(stdout, "{}", Paint::white("debug: ")).map_err(fmt)?;
                    DebugState::InLine
                };
            }

            match self.debug_state {
                DebugState::Off => {
                    stdout.write_all(s.as_bytes()).map_err(fmt)?;
                    s = "";
                },
                DebugState::InLine => {
                    if let Some(linepos) = s.find('\n') {
                        let linepos = linepos + 1;
                        stdout.write_all(s[..linepos].as_bytes()).map_err(fmt)?;
                        s = &s[linepos..];
                        self.debug_state = DebugState::LineStart;
                    } else {
                        stdout.write_all(s.as_bytes()).map_err(fmt)?;
                        s = "";
                    }
                },
                DebugState::Hidden => break,
                // handled above
                _ => unreachable!()
            }
        }

        return Ok(())
    }
}