-
Notifications
You must be signed in to change notification settings - Fork 19
/
pervasive.rb
177 lines (153 loc) · 6.18 KB
/
pervasive.rb
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
# Copyright (c) [2019-2023] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
require "y2storage/encryption_type"
require "y2storage/encryption_processes/base"
require "y2storage/encryption_processes/secure_key"
require "yast2/execute"
require "yast"
Yast.import "Mode"
module Y2Storage
module EncryptionProcesses
# Encryption process that allows to create and identify a volume encrypted
# with Pervasive Encryption.
#
# For more information, see
# https://www.ibm.com/support/knowledgecenter/linuxonibm/liaaf/lnz_r_dccnt.html
class Pervasive < Base
# Location of the zkey command
ZKEY = "/usr/bin/zkey".freeze
private_constant :ZKEY
attr_reader :apqns
# Creates an encryption layer over the given block device
#
# @param blk_device [Y2Storage::BlkDevice]
# @param dm_name [String]
# @param apqns [Array<Apqn>] APQNs to use for generating the secure key
#
# @return [Encryption]
def create_device(blk_device, dm_name, apqns: [])
@secure_key = SecureKey.for_device(blk_device)
@apqns = apqns
if @secure_key
# Should we discard the key if it's not a LUKS2 one?
# Or maybe we should modify the secure key in that case?
name_from_key = @secure_key.dm_name(blk_device)
dm_name = name_from_key if name_from_key
end
super(blk_device, dm_name)
end
# @see Base#pre_commit
#
# If there is no secure key to be used for this device, a new key is
# generated.
#
# @param device [Encryption] encryption that will be created in the system
def pre_commit(device)
# For the time being, we will always generate a new key for each device
# if there was not a preexisting one. In the future we can extend the
# API to allow sharing a new key among several volumes.
@secure_key ||= generate_secure_key(device)
master_key_file = @secure_key.filename
sector_size = sector_size_for(device.blk_device)
# NOTE: The options cipher and key-size could also be influenced by setting
# Encryption#cipher and Encryption#key_size. If those attributes have any value, the
# correspoding format options are prepended to Encryption#format_options by libstorage-ng.
device.format_options = "--master-key-file #{master_key_file.shellescape} --key-size 1024 "\
"--cipher paes-xts-plain64"
device.format_options += " --sector-size #{sector_size}" if sector_size
device.format_options += " --pbkdf pbkdf2"
end
# @see Base#post_commit
#
# Adds the device to the secure key if needed and executes the
# extra commands reported by a call to "zkey cryptsetup".
#
# @param device [Encryption] encryption that has just been created in the system
def post_commit(device)
@secure_key.add_device_and_write(device) unless @secure_key.for_device?(device)
zkey_cryptsetup_output = execute_zkey_cryptsetup(device)
commands = zkey_cryptsetup_output[1..-1]
return if commands.nil?
commands.each do |command|
args = command.split
if args.any? { |arg| "setvp".casecmp(arg) == 0 }
args += ["--key-file", "-"]
Yast::Execute.locally(*args, stdin: device.password, recorder: cheetah_recorder)
else
Yast::Execute.locally(*args)
end
end
end
# @see Base#finish_installation
#
# Copies the keys from the zkey repository of the inst-sys to the
# repository of the target system.
def finish_installation
secure_key.copy_to_repository(Yast::Installation.destdir)
end
# Class to prevent Yast::Execute from leaking to the logs the password
# provided via stdin
class Recorder < Cheetah::DefaultRecorder
# To prevent leaking stdin, just do nothing
def record_stdin(_stdin); end
end
# @see Base#encryption_type
def encryption_type
EncryptionType::LUKS2
end
private
# @return [SecureKey] master key used to encrypt the device
attr_reader :secure_key
# @return [Array<String>] lines of the output of the "zkey cryptsetup"
# command executed during the pre-commit phase
attr_reader :zkey_cryptsetup_output
# Custom Cheetah recorder to prevent leaking the password to the logs
#
# @return [Recorder]
def cheetah_recorder
Recorder.new(Yast::Y2Logger.instance)
end
# Generates a new secure key for the given encryption device and
# registers it into the keys database of the system. The secure
# does not include the volume since that may not exist yet.
#
# @param device [Encryption]
# @return [SecureKey]
def generate_secure_key(device)
key_name = "YaST_#{device.dm_table_name}"
key = SecureKey.generate(
key_name,
sector_size: sector_size_for(device.blk_device),
apqns: apqns
)
log.info "Generated secure key #{key.name}"
key
end
# Executes the "zkey cryptsetup" used to get the list of commands that
# must be executed during the pervasive encryption process
#
# @return [Array<String>]
def execute_zkey_cryptsetup(device)
name = secure_key.plain_name(device)
command = [ZKEY, "cryptsetup", "--volumes", name]
Yast::Execute.locally(*command, stdout: :capture)&.lines&.map(&:strip) || []
end
end
end
end