File shim-posttrans.spec of Package shim-posttrans
#
# spec file for package pesign-obs-integration
#
# Copyright (c) 2025 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
Name: shim-posttrans
Version: 0.1
Release: 0
Summary: Testing lua script for shim posttrans
License: GPL-2.0-or-later
Group: Development/Tools/Other
Source0: Microsoft_Corporation_UEFI_CA_2011.hex
%description
Testing lua script for shim posttrans
%prep
%build
%install
# install MS certificate hex, just for packaging for testing
install -d %{buildroot}/%{_sysconfdir}/uefi/certs/
install -m 644 %{SOURCE0} %{buildroot}/%{_sysconfdir}/uefi/certs/
%pretrans -p <lua>
-- Using Lua
print("INFO: Current Lua Version: " .. tostring(_VERSION))
-- ==========================================================================================
-- This pretrans script verifies that the UEFI db should have the necessary certificate to
-- allow the shim binary to boot.
-- The installation will be aborted if the db is missing the target certificate. To proceed,
-- the user must enroll the target certificate in the db or disable UEFI Secure Boot.
-- ==========================================================================================
local db_filename = "/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f"
-- The db file existence check
-- Use pcall to execute rpm.open to prevent errors from being thrown when
-- the file cannot be found, causing RPM to fail.
local success, result = pcall(rpm.open, db_filename, "rb")
local f_check = nil
if not success then
-- pcall catches errors (e.g. "No such file or directory")
print("WARNING: Attempt to open db EFI variable file failed. Error message: " .. tostring(result))
print("WARNING: This usually means the system is not booted in UEFI mode. Skipping all db check steps.")
return 0
else
-- If pcall succeeds, result may be an archive handle or nil (depending on the behavior of rpm.open)
f_check = result
if not f_check then
-- The archive does not exist, but rpm.open returns nil
print("WARNING: db EFI variable file does not exist (rpm.open returned nil). Skipping db check steps.")
return 0
else
-- If the file exists and is successfully opened,
-- close the handle immediately so that subsequent code can open it again.
f_check:close()
end
end
-- ==========================================================================================
-- This is the hardcoded target certificate content used to check for its existence.
-- HEX_CONTENT=$(xxd -p taget_certificate.der | tr -d '\n') && echo "$HEX_CONTENT"
-- ==========================================================================================
-- Only the DER format is supported
local TARGET_CERT_HEXES = {
-- Certificate #1, Microsoft Corporation UEFI CA 2011
"30820610308203f8a003020102020a6108d3c4000000000004300d06092a864886f70d01010b0500308191310b3009060355040613025553311330110603550408130a57617368696e67746f6e3110300e060355040713075265646d6f6e64311e301c060355040a13154d6963726f736f667420436f72706f726174696f6e313b3039060355040313324d6963726f736f667420436f72706f726174696f6e205468697264205061727479204d61726b6574706c61636520526f6f74301e170d3131303632373231323234355a170d3236303632373231333234355a308181310b3009060355040613025553311330110603550408130a57617368696e67746f6e3110300e060355040713075265646d6f6e64311e301c060355040a13154d6963726f736f667420436f72706f726174696f6e312b3029060355040313224d6963726f736f667420436f72706f726174696f6e2055454649204341203230313130820122300d06092a864886f70d01010105000382010f003082010a0282010100a5086c4cc745096a4b0ca4c0877f06750c43015464e0167f07ed927d0bb273bf0c0ac64a4561a0c5162d96d3f52ba0fb4d499b4180903cb954fde6bcd19dc4a4188a7f418a5c59836832bb8c47c9ee71bc214f9a8a7cff443f8d8f32b22648ae75b5eec94c1e4a197ee4829a1d78774d0cb0bdf60fd316d3bcfa2ba551385df5fbbadb7802dbffec0a1b96d583b81913e9b6c07b407be11f2827c9faef565e1ce67e947ec0f044b27939e5dab2628b4dbf3870e2682414c933a40837d558695ed37cedc1045308e74eb02a876308616f631559eab22b79d70c61678a5bfd5ead877fba86674f71581222042222ce8bef547100ce503558769508ee6ab1a201d50203010001a382017630820172301206092b060104018237150104050203010001302306092b060104018237150204160414f8c16bb77f77534af325371d4ea1267b0f207080301d0603551d0e0416041413adbf4309bd82709c8cd54f316ed522988a1bd4301906092b0601040182371402040c1e0a00530075006200430041300b0603551d0f040403020186300f0603551d130101ff040530030101ff301f0603551d2304183016801445665243e17e5811bfd64e9e2355083b3a226aa8305c0603551d1f045530533051a04fa04d864b687474703a2f2f63726c2e6d6963726f736f66742e636f6d2f706b692f63726c2f70726f64756374732f4d6963436f725468695061724d6172526f6f5f323031302d31302d30352e63726c306006082b0601050507010104543052305006082b060105050730028644687474703a2f2f7777772e6d6963726f736f66742e636f6d2f706b692f63657274732f4d6963436f725468695061724d6172526f6f5f323031302d31302d30352e637274300d06092a864886f70d01010b05000382020100350842ff30cccef7760cad1068583529463276277cef124127421b4aaa6d813848591355f3e95834a6160b82aa5dad82da808341068fb41df203b9f31a5d1bf15090f9b3558442281c20bdb2ae5114c5c0ac9795211c90db0ffc779e95739188cabdbd52b905500ddf579ea061ed0de56d25d9400f1740c8cea34ac24daf9a121d08548fbdc7bcb92b3d492b1f32fc6a21694f9bc87e4234fc3606178b8f2040c0b39a257527cdc903a3f65dd1e736547ab950b5d312d107bfbb74dfdc1e8f80d5ed18f42f14166b2fde668cb023e5c784d8edeac13382ad564b182df1689507cdcff072f0aebbdd8685982c214c332bf00f4af06887b592553275a16a826a3ca32511a4edadd704aecbd84059a084d1954c6291221a741d8c3d470e44a6e4b09b3435b1fab653a82c81eca40571c89db8bae81b4466e447540e8e567fb39f1698b286d0683e9023b52f5e8f50858dc68d825f41a1f42e0de099d26c75e4b669b52186fa07d1f6e24dd1daad2c77531e253237c76c52729586b0f135616a19f5b23b815056a6322dfea289f94286271855a182ca5a9bf830985414a64796252fc826e441941a5c023fe596e3855b3c3e3fbb47167255e22522b1d97be703062aa3f71e9046c3000dd61989e30e352762037115a6efd027a0a0593760f83894b8e07870f8ba4c868794f6e0ae0245ee65c2b6a37e69167507929bf5a6bc598358",
-- Certificate #2, openSUSE Secure Boot CA 2013
"308204743082035ca003020102020101300d06092a864886f70d01010b05003081813120301e06035504030c176f70656e535553452053656375726520426f6f74204341310b30090603550406130244453112301006035504070c094e7572656d6265726731193017060355040a0c106f70656e535553452050726f6a6563743121301f06092a864886f70d01090116126275696c64406f70656e737573652e6f7267301e170d3133303832363136313230375a170d3335303732323136313230375a3081813120301e06035504030c176f70656e535553452053656375726520426f6f74204341310b30090603550406130244453112301006035504070c094e7572656d6265726731193017060355040a0c106f70656e535553452050726f6a6563743121301f06092a864886f70d01090116126275696c64406f70656e737573652e6f726730820122300d06092a864886f70d01010105000382010f003082010a0282010100dedf61927aa4fe83d17d3b680eb1a7f04e9293fc473e702d4e88dc9a9efa33b4a6db0e23c10da8c1d565048404ff3a48184f3932e4ca4ef9049e9f0fcd205d61aba700d8a5ff2b7fbee847c32f5b02c8bbde8e1ae946d386efff889990eb1089b88b3f3ea807c6557a6ed35ffc833c3d16ed26c5137392b1701e2295c8006c257646f1a2d9d0b098680fa72db10d6789ca944aea12c59155767f6c7a2ef918899ff8f42443d5356acb000e2eed4be25d09d81b9770999e5a6fa681a89da958767d697182d3ba3a96439bf0da15c64ee9c815b9e9cbc7e471ceea101b6bc42a7001a952b417de0052cf7de4fd0f4d0318b29028d46fc4ae56bc366049468b6b0b0203010001a381f43081f1300f0603551d130101ff040530030101ff301d0603551d0e041604146842600de22c4c477e95be23dfea9513e59717623081ae0603551d230481a63081a380146842600de22c4c477e95be23dfea9513e5971762a18187a481843081813120301e06035504030c176f70656e535553452053656375726520426f6f74204341310b30090603550406130244453112301006035504070c094e7572656d6265726731193017060355040a0c106f70656e535553452050726f6a6563743121301f06092a864886f70d01090116126275696c64406f70656e737573652e6f7267820101300e0603551d0f0101ff040403020186300d06092a864886f70d01010b050003820101008aa389c28ed9f9820bf333cee9191717a36580cd33ae06515629b638877bf49dfc288eaae053120e3a60c706d83a61763b7708f494a48c7c473a99d8849b17cc20622ee276e4c6360d26e92e53350afb3a359345c39382c10bf308e9571f5937a9d06c69fb68ea7f3bafd3f759278ed4c79673f40c0af73ee4af6c8cc77a6f0979f4411fe36f11fb3e6cb1a07be492b7caf932f5dec3b0737de3b3825dcdec61dcfe0c3ec6b5e76c2d5d9273ffedaa6aa99b669e5e3a6d70b031c0cedf2f2110680c87f377a033310a0f15f6ee3288c59a5371cd0d1aa12889d0bff656ac4b3b36062b01c5ebe5dc72833d94ac288313fbc15d279c13f6325ff61f4ab73e538a",
}
-- Check if the TARGET_CERT_HEXES array is empty
if #TARGET_CERT_HEXES == 0 then
print("INFO: certificate list is empty. Skipping certificate check.")
-- Exiting safely as the certificate list is empty.
return 0
else
-- Check if the Hex string for certificate is valid
for i, cert_hex in ipairs(TARGET_CERT_HEXES) do
if #cert_hex % 2 ~= 0 then
print("Error: The length of hard-coded hex string for certificate #" .. i .. " must be an even number.")
error("The Hex string is invalid. The transaction is being aborted in the pretrans script.")
end
end
end
-- =========================================================================
-- Helper functions
-- =========================================================================
-- Convert hexadecimal string to original binary string
local function hex_to_binary(hex)
local binary = ""
for i = 1, #hex, 2 do
local byte_hex = hex:sub(i, i + 1)
binary = binary .. string.char(tonumber(byte_hex, 16))
end
return binary
end
-- =========================================================================
-- Main logic for checking if the db has any target certificate
-- =========================================================================
-- Read existing db contents
local db_content = ""
do
-- The db file is now confirmed to exist, open it again to read the contents
local f = rpm.open(db_filename, "rb")
if f then
local chunks = {}
local CHUNK_SIZE = 4096
local raw_content = ""
local chunk = f:read(CHUNK_SIZE)
while chunk do
-- If an empty string is read, it means EOF has been reached and the loop is exited.
if chunk == "" then
break
end
table.insert(chunks, chunk)
chunk = f:read(CHUNK_SIZE)
end
raw_content = table.concat(chunks)
f:close()
-- Skip the first 4 bytes (EFI attributes)
if #raw_content > 4 then
-- Truncate from the 5th byte to the end
db_content = string.sub(raw_content, 5)
print("INFO: Successfully read existing db content")
else
-- The file is too small or only has attributes, so it is considered blank.
db_content = ""
print("WARNING: db file content length is abnormal (<= 4 bytes). Treated as blank.")
end
end
end
-- Check all target certificates
for i, cert_hex in ipairs(TARGET_CERT_HEXES) do
local target_binary_content = hex_to_binary(cert_hex)
-- Perform binary string matching
local start_pos, end_pos = db_content:find(target_binary_content, 1, true)
if start_pos then
-- Success: Certificate exist in db
-- Return 0 to allow the RPM transaction to continue
print("Target certificate #" .. i .. " was found in the db variable. Proceed with install.")
return 0
end
end
-- Certificate not present in db
print("WARNING: The target certificate binary was not found in the db variable.")
print("Please add the appropriate certificate to the db or disable UEFI secure boot.")
-- Secure Boot status check: We only proceed with installation if the certificate is not present in the db and Secure Boot is disabled.
local sb_filename = "/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c"
local success_sb, result_sb = pcall(rpm.open, sb_filename, "rb")
if not success_sb or not result_sb then
-- If the file is missing, it typically means the system is not UEFI, or Secure Boot is disabled/the variable is absent.
print("WARNING: SecureBoot EFI variable file does not exist. Proceed with install.")
else
local f_sb = result_sb
local raw_content_sb = ""
local sb_status = 0
-- Read file contents
local chunk_sb = f_sb:read(4096)
while chunk_sb do
if chunk_sb == "" then break end
raw_content_sb = raw_content_sb .. chunk_sb
chunk_sb = f_sb:read(4096)
end
f_sb:close()
-- SecureBoot status check
if #raw_content_sb >= 5 then
-- Skip the first 4-byte attribute header and read the 5th byte (status byte)
sb_status = string.byte(raw_content_sb, 5)
if sb_status == 0x00 then
print("INFO: Since Secure Boot is DISABLED, proceed with install.")
return 0
elseif sb_status == 0x01 then
error("Fatal error: Secure Boot is ENABLED (status = 0x01), but the target certificate was not found in the db. Aborting installation.")
else
error("Fatal error: Secure Boot status is unrecognized (0x" .. string.format("%02x", sb_status) .. "). Aborting installation.")
end
else
error("Fatal error: SecureBoot variable content is too short to determine status. Aborting installation.")
end
end
%files
%dir %{_sysconfdir}/uefi/
%dir %{_sysconfdir}/uefi/certs/
%{_sysconfdir}/uefi/certs/*.hex
%changelog