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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! UEFI Nvram Variable Services
use crate::uefi::signing::WIN_CERTIFICATE_UEFI_GUID;
use crate::uefi::time::EFI_TIME;
use bitfield_struct::bitfield;
use guid::Guid;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
/// UEFI spec 8.2 - Variable Services
#[bitfield(u32)]
#[derive(Eq, PartialEq)]
pub struct EfiVariableAttributes {
pub non_volatile: bool,
pub bootservice_access: bool,
pub runtime_access: bool,
pub hardware_error_record: bool,
pub authenticated_write_access: bool,
pub time_based_authenticated_write_access: bool,
pub append_write: bool,
pub enhanced_authenticated_access: bool,
#[bits(24)]
_reserved: u32,
}
impl EfiVariableAttributes {
pub const DEFAULT_ATTRIBUTES: EfiVariableAttributes = EfiVariableAttributes::new()
.with_non_volatile(true)
.with_bootservice_access(true)
.with_runtime_access(true);
pub const DEFAULT_ATTRIBUTES_VOLATILE: EfiVariableAttributes = EfiVariableAttributes::new()
.with_bootservice_access(true)
.with_runtime_access(true);
pub const DEFAULT_ATTRIBUTES_TIME_BASED_AUTH: EfiVariableAttributes =
Self::DEFAULT_ATTRIBUTES.with_time_based_authenticated_write_access(true);
pub fn contains_unsupported_bits(&self) -> bool {
u32::from(*self)
& !u32::from(
Self::new()
.with_non_volatile(true)
.with_bootservice_access(true)
.with_runtime_access(true)
.with_hardware_error_record(true)
.with_authenticated_write_access(true)
.with_time_based_authenticated_write_access(true)
.with_append_write(true)
.with_enhanced_authenticated_access(true),
)
!= 0
}
}
/// UEFI spec 8.2
#[derive(FromBytes, FromZeroes, AsBytes)]
#[repr(C)]
pub struct EFI_VARIABLE_AUTHENTICATION_2 {
/// Components Pad1, Nanosecond, TimeZone, Daylight and Pad2 shall be set to
/// 0. This means that the time shall always be expressed in GMT.
pub timestamp: EFI_TIME,
/// Provides the authorization for the variable access. Only a CertType of
/// EFI_CERT_TYPE_PKCS7_GUID is accepted.
pub auth_info: WIN_CERTIFICATE_UEFI_GUID,
}
impl EFI_VARIABLE_AUTHENTICATION_2 {
/// A "dummy" header that doesn't actually include a valid cert.
///
/// This header is used during parts of the pre-boot setup to inject
/// `TIME_BASED_AUTHENTICATED_WRITE_ACCESS` from within the UEFI device
/// itself.
pub const DUMMY: Self = {
use crate::uefi::signing::EFI_CERT_TYPE_PKCS7_GUID;
use crate::uefi::signing::WIN_CERTIFICATE;
use crate::uefi::signing::WIN_CERT_TYPE_EFI_GUID;
EFI_VARIABLE_AUTHENTICATION_2 {
timestamp: EFI_TIME::ZEROED,
auth_info: WIN_CERTIFICATE_UEFI_GUID {
header: WIN_CERTIFICATE {
// `length` includes both the header itself, and the cert
// payload (which has len 0 in the dummy header)
length: size_of::<WIN_CERTIFICATE_UEFI_GUID>() as u32,
revision: 0x0200,
certificate_type: WIN_CERT_TYPE_EFI_GUID,
},
cert_type: EFI_CERT_TYPE_PKCS7_GUID,
},
}
};
}
/// UEFI spec 32.4.1
pub mod signature_list {
use guid::Guid;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
#[derive(Debug, PartialEq, Eq, FromBytes, FromZeroes, AsBytes)]
#[repr(C)]
pub struct EFI_SIGNATURE_LIST {
/// Type of the signature. GUID signature types are defined in "Related
/// Definitions" below.
pub signature_type: Guid,
/// Total size of the signature list, including this header.
pub signature_list_size: u32,
/// Size of the signature header which precedes the array of signatures.
///
/// > NOTE: a careful reading of the UEFI spec uncovers that this field
/// > is _always_ zero. Why? Excellent question.
pub signature_header_size: u32,
/// Size of each signature. Must be at least the size of EFI_SIGNATURE_DATA.
pub signature_size: u32,
// Header before the array of signatures. The format of this header is
// specified by the SignatureType.
//
// > NOTE: because SignatureHeaderSize is always zero, this array is
// > always zero sized...
//
// UINT8 SignatureHeader[SignatureHeaderSize];
//
// An array of signatures. Each signature is SignatureSize bytes in
// length. The format of the signature is defined by the SignatureType.
//
// EFI_SIGNATURE_DATA Signatures[…][SignatureSize];
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromBytes, FromZeroes, AsBytes,
)]
#[repr(C)]
pub struct EFI_SIGNATURE_DATA {
/// An identifier which identifies the agent which added the signature to
/// the list.
pub signature_owner: Guid,
// UINT8 SignatureData[…];
}
pub const EFI_CERT_SHA256_GUID: Guid =
Guid::from_static_str("c1c41626-504c-4092-aca9-41f936934328");
pub const EFI_CERT_X509_GUID: Guid =
Guid::from_static_str("a5c059a1-94e4-4aa7-87b5-ab155c2bf072");
}
/// Check if the specified variable is a secure boot policy variable, as
/// specified by the UEFI spec in section 3.3 Globally Defined Variables, under
/// the details of `SetupMode`.
pub fn is_secure_boot_policy_var(vendor: Guid, name: &ucs2::Ucs2LeSlice) -> bool {
let secure_boot_policy_vars = [
vars::PK(),
vars::KEK(),
// TODO: add OsRecoveryOrder, OsRecovery####
];
let is_secure_boot_policy_var = secure_boot_policy_vars
.into_iter()
.any(|v| v == (vendor, name));
is_secure_boot_policy_var || vendor == vars::IMAGE_SECURITY_DATABASE_GUID
}
/// UEFI spec 3.3 - Table 3-1
///
/// Due to the Rust compiler not having built-in support for defining
/// wide-string literals, and me not wanting to yak-shave a proc macro
/// implementation that emits valid Utf16LeSlices at compile time, these
/// "constants" are actually methods that can only be called at runtime.
#[allow(dead_code)] // no live code - just a bunch of constants
pub mod vars {
use guid::Guid;
/// UEFI spec 3.3 - Globally Defined Variables
pub const EFI_GLOBAL_VARIABLE: Guid =
Guid::from_static_str("8BE4DF61-93CA-11D2-AA0D-00E098032B8C");
/// UEFI spec 32.6.1 - UEFI Image Variable GUID & Variable Name
pub const IMAGE_SECURITY_DATABASE_GUID: Guid =
Guid::from_static_str("d719b2cb-3d3a-4596-a3bc-dad00e67656f");
defn_nvram_var!(SECURE_BOOT = (EFI_GLOBAL_VARIABLE, "SecureBoot"));
defn_nvram_var!(SETUP_MODE = (EFI_GLOBAL_VARIABLE, "SetupMode"));
defn_nvram_var!(PK = (EFI_GLOBAL_VARIABLE, "PK"));
defn_nvram_var!(KEK = (EFI_GLOBAL_VARIABLE, "KEK"));
defn_nvram_var!(DBDEFAULT = (EFI_GLOBAL_VARIABLE, "dbDefault"));
defn_nvram_var!(DB = (IMAGE_SECURITY_DATABASE_GUID, "db"));
defn_nvram_var!(DBX = (IMAGE_SECURITY_DATABASE_GUID, "dbx"));
}