Skip to content

Commit

Permalink
support s390 secure boot (jsc#SLE-9425)
Browse files Browse the repository at this point in the history
This extends the existing secure boot logic to s390.

A number of system-dependend logic has been moved into a new Systeminfo class.
  • Loading branch information
wfeldt committed Feb 21, 2020
1 parent 7d67f20 commit 637ed78
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 23 deletions.
42 changes: 36 additions & 6 deletions src/lib/bootloader/grub2.rb
Expand Up @@ -7,6 +7,7 @@
require "bootloader/device_map"
require "bootloader/stage1"
require "bootloader/grub_install"
require "bootloader/systeminfo"

Yast.import "Arch"
Yast.import "BootStorage"
Expand All @@ -17,6 +18,7 @@ module Bootloader
class Grub2 < Grub2Base
attr_reader :stage1
attr_reader :device_map
attr_accessor :secure_boot

def initialize
super
Expand All @@ -31,6 +33,8 @@ def initialize
def read
super

@secure_boot = Sysconfig.from_system.secure_boot

begin
stage1.read
rescue Errno::ENOENT
Expand Down Expand Up @@ -65,7 +69,9 @@ def write

# powernv must not call grub2-install (bnc#970582)
unless Yast::Arch.board_powernv
failed = @grub_install.execute(devices: stage1.devices, trusted_boot: trusted_boot)
failed = @grub_install.execute(
devices: stage1.devices, secure_boot: @secure_boot, trusted_boot: trusted_boot
)
failed.each { |f| stage1.remove_device(f) }
stage1.write
end
Expand All @@ -77,6 +83,8 @@ def write
def propose
super

@secure_boot = Systeminfo.secure_boot_active?

stage1.propose
# for GPT add protective MBR flag otherwise some systems won't
# boot, safer option for legacy booting (bnc#872054)
Expand All @@ -88,6 +96,8 @@ def propose
def merge(other)
super

@secure_boot = other.secure_boot unless other.secure_boot.nil?

@device_map = other.device_map if !other.device_map.empty?

stage1.merge(other.stage1)
Expand All @@ -100,12 +110,12 @@ def summary(simple_mode: false)
Yast::Builtins.sformat(
_("Boot Loader Type: %1"),
"GRUB2"
),
Yast::Builtins.sformat(
_("Enable Trusted Boot: %1"),
trusted_boot ? _("yes") : _("no")
)
]

result.push secure_boot_summary if Systeminfo.secure_boot_available?
result.push trusted_boot_summary if Systeminfo.trusted_boot_available?

locations_val = locations
if !locations_val.empty?
result << Yast::Builtins.sformat(
Expand Down Expand Up @@ -142,12 +152,32 @@ def packages
# FIXME: refactor with injection like super(prewrite: prewrite, sysconfig = ...)
# overwrite BootloaderBase version to save trusted boot
def write_sysconfig(prewrite: false)
sysconfig = Bootloader::Sysconfig.new(bootloader: name, trusted_boot: trusted_boot)
sysconfig = Bootloader::Sysconfig.new(
bootloader: name, secure_boot: @secure_boot, trusted_boot: trusted_boot
)
prewrite ? sysconfig.pre_write : sysconfig.write
end

private

def secure_boot_summary
_("Secure Boot:") + " " + (@secure_boot ? _("enabled") : _("disabled")) + " " +
if @secure_boot
"<a href=\"disable_secure_boot\">(" + _("disable") + ")</a>"
else
"<a href=\"enable_secure_boot\">(" + _("enable") + ")</a>"
end
end

def trusted_boot_summary
_("Trusted Boot:") + " " + (trusted_boot ? _("enabled") : _("disabled")) + " " +
if trusted_boot
"<a href=\"disable_trusted_boot\">(" + _("disable") + ")</a>"
else
"<a href=\"enable_trusted_boot\">(" + _("enable") + ")</a>"
end
end

# Checks if syslinux package should be included
#
# Needed for generic_mbr binary files, but it must not be required in inst-sys as inst-sys have
Expand Down
15 changes: 5 additions & 10 deletions src/lib/bootloader/grub2_widgets.rb
Expand Up @@ -6,6 +6,7 @@
require "bootloader/device_map_dialog"
require "bootloader/serial_console"
require "bootloader/cpu_mitigations"
require "bootloader/systeminfo"
require "cfa/matcher"

Yast.import "BootStorage"
Expand All @@ -14,6 +15,7 @@
Yast.import "Report"
Yast.import "UI"
Yast.import "Mode"
Yast.import "Arch"

module Bootloader
# Adds to generic widget grub2 specific helpers
Expand Down Expand Up @@ -326,7 +328,7 @@ def label
end

def help
_("<p><b>Enable Secure Boot Support</b> if checked enables UEFI Secure Boot support.</p>")
_("<p><b>Enable Secure Boot Support</b> if checked enables Secure Boot support.</p>")
end

def init
Expand Down Expand Up @@ -978,18 +980,11 @@ def generic_mbr_widget?
end

def secure_boot_widget?
(Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.aarch64) && grub2.name == "grub2-efi"
Systeminfo.secure_boot_available?
end

def trusted_boot_widget?
return false if !(Yast::Arch.x86_64 || Yast::Arch.i386)

return true if grub2.name == "grub2"

# for details about grub2 efi trusted boot support see FATE#315831
return File.exist?("/dev/tpm0") if grub2.name == "grub2-efi"

false
Systeminfo.trusted_boot_available?
end

def pmbr_widget?
Expand Down
7 changes: 5 additions & 2 deletions src/lib/bootloader/grub_install.rb
Expand Up @@ -2,6 +2,7 @@

require "yast"
require "yast2/execute"
require "bootloader/systeminfo"

Yast.import "Arch"
Yast.import "Report"
Expand All @@ -25,7 +26,9 @@ def initialize(efi: false)
# @param trusted_boot [Boolean] if trusted boot variant should be used
# @return [Array<String>] list of devices for which install failed
def execute(devices: [], secure_boot: false, trusted_boot: false)
raise "cannot have secure boot without efi" if secure_boot && !efi
if secure_boot && !Systeminfo.secure_boot_available?
raise "cannot enable secure boot on this machine"
end

cmd = basic_cmd(secure_boot, trusted_boot)

Expand Down Expand Up @@ -71,7 +74,7 @@ def report_failure(exception)
# creates basic command for grub2 install without specifying any stage1
# locations
def basic_cmd(secure_boot, trusted_boot)
if secure_boot && !Yast::Arch.aarch64
if Systeminfo.shim_needed?
cmd = ["/usr/sbin/shim-install", "--config-file=/boot/grub2/grub.cfg"]
else
cmd = ["/usr/sbin/grub2-install", "--target=#{target}"]
Expand Down
33 changes: 29 additions & 4 deletions src/lib/bootloader/proposal_client.rb
Expand Up @@ -4,12 +4,14 @@
require "bootloader/exceptions"
require "bootloader/main_dialog"
require "bootloader/bootloader_factory"
require "bootloader/systeminfo"
require "yast2/popup"

Yast.import "BootArch"

module Bootloader
# Proposal client for bootloader configuration
# rubocop:disable Metrics/ClassLength
class ProposalClient < ::Installation::ProposalClient
# Error when during update media is booted by different technology than target system.
class MismatchBootloader < RuntimeError
Expand Down Expand Up @@ -72,7 +74,11 @@ def initialize
"enable_boot_mbr",
"disable_boot_mbr",
"enable_boot_boot",
"disable_boot_boot"
"disable_boot_boot",
"enable_secure_boot",
"disable_secure_boot",
"enable_trusted_boot",
"disable_trusted_boot"
].freeze

def make_proposal(attrs)
Expand All @@ -94,7 +100,7 @@ def ask_user(param)
case chosen_id
when *PROPOSAL_LINKS
value = (chosen_id =~ /enable/) ? true : false
option = chosen_id[/(enable|disable)_boot_(.*)/, 2]
option = chosen_id[/(enable|disable)_(.*)/, 2]
single_click_action(option, value)
else
settings = export_settings
Expand Down Expand Up @@ -337,10 +343,28 @@ def handle_errors(ret)

def single_click_action(option, value)
stage1 = ::Bootloader::BootloaderFactory.current.stage1
devices = (option.to_sym == :mbr) ? stage1.boot_disk_names : stage1.boot_partition_names
case option
when "boot_mbr"
devices = stage1.boot_disk_names
when "boot_boot"
devices = stage1.boot_partition_names
when "trusted_boot"
::Bootloader::BootloaderFactory.current.trusted_boot = value
when "secure_boot"
::Bootloader::BootloaderFactory.current.secure_boot = value
if value && Yast::Arch.s390
Yast2::Popup.show(
_(
"Please make sure to also enable Secure Boot in the HMC.\n" \
"Otherwise this machine may not boot."
),
headline: :warning, buttons: :ok
)
end
end
log.info "single_click_action #{option} #{value.inspect} #{devices}"

devices.each do |device|
devices&.each do |device|
value ? stage1.add_udev_device(device) : stage1.remove_device(device)
end

Expand All @@ -355,4 +379,5 @@ def update_required_packages
Yast::PackagesProposal.AddResolvables("yast2-bootloader", :package, bl.packages)
end
end
# rubocop:enable Metrics/ClassLength
end
3 changes: 2 additions & 1 deletion src/lib/bootloader/sysconfig.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "yast"
require "bootloader/systeminfo"

Yast.import "Arch"

Expand Down Expand Up @@ -33,7 +34,7 @@ def self.from_system
bootloader = Yast::SCR.Read(AGENT_PATH + "LOADER_TYPE")
# propose secure boot always to true (bnc#872054), otherwise respect user choice
# but only on architectures that support it
secure_boot = if Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.aarch64
secure_boot = if Systeminfo.secure_boot_supported?
Yast::SCR.Read(AGENT_PATH + "SECURE_BOOT") != "no"
else
false
Expand Down
72 changes: 72 additions & 0 deletions src/lib/bootloader/systeminfo.rb
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require "yast"
require "bootloader/bootloader_factory"

Yast.import "Arch"

module Bootloader
# provide system and architecture dependent information
class Systeminfo
include Yast::Logger

class << self
# true if secure boot is currently active
def secure_boot_active?
efi_supported? || s390_secure_boot_active?
end

# true if boot config uses secure boot
def secure_boot_used?
::Bootloader::BootloaderFactory.current.secure_boot
end

# true if secure boot is (in principle) supported
def secure_boot_supported?
efi_supported? || s390_secure_boot_supported?
end

# true if secure boot setting is available for current boot config
def secure_boot_available?
efi_used? || s390_secure_boot_supported?
end

# true if trusted boot setting is available for current boot config
def trusted_boot_available?
# for details about grub2 efi trusted boot support see FATE#315831
(
::Bootloader::BootloaderFactory.current.name == "grub2" &&
(Yast::Arch.x86_64 || Yast::Arch.i386)
) || (
::Bootloader::BootloaderFactory.current.name == "grub2-efi" &&
File.exist?("/dev/tpm0")
)
end

# true if UEFI will be used for booting
def efi_used?
::Bootloader::BootloaderFactory.current.name == "grub2-efi"
end

# true if system can (in principle) boot via UEFI
def efi_supported?
Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.aarch64
end

# true if shim has to be used
def shim_needed?
(Yast::Arch.x86_64 || Yast::Arch.i386) && secure_boot_used? && efi_used?
end

# true if s390 machine has secure boot support
def s390_secure_boot_supported?
Yast::Arch.s390
end

# true if 390x machine has secure boot enabled
def s390_secure_boot_active?
false
end
end
end
end

0 comments on commit 637ed78

Please sign in to comment.