-
Notifications
You must be signed in to change notification settings - Fork 22
/
grub2pwd_test.rb
263 lines (206 loc) · 7.87 KB
/
grub2pwd_test.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
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
#! /usr/bin/env rspec --format doc
require_relative "./test_helper"
require "bootloader/grub2pwd"
describe Bootloader::GRUB2Pwd do
before do
# by default use initial stage to get proposed values
Yast.import "Stage"
allow(Yast::Stage).to receive(:initial).and_return(true)
end
def mock_file_presence(exists)
Yast.import "FileUtils"
expect(Yast::FileUtils).to receive(:Exists).with("/etc/grub.d/42_password")
.and_return(exists)
end
ENCRYPTED_PASSWORD = "grub.pbkdf2.sha512.10000.774E325959D6D7BCFB7384A0245674D83D0D540A89C02FEA81E35489F8DE7ADFD93988190AD9857A0FFF363825DDF97C8F4E658D8CC49FC4A22C053B08AB3EFE.6FB19FF26FD03D85C40A33D8BA7C04E72EDE3DD5D7080C177553A4FED370F71C579AF0B15B3B93ECECEA355469A4B6D0560BFB53ED35DDA0B80F5363BFBD54E4".freeze
FILE_CONTENT_RESTRICTED = "#! /bin/sh\n" \
"exec tail -n +3 $0\n" \
"# File created by YaST and next YaST run probably overwrite it\n" \
"set superusers=\"root\"\n" \
"password_pbkdf2 root #{ENCRYPTED_PASSWORD}\n" \
"export superusers\n".freeze
FILE_CONTENT_UNRESTRICTED = FILE_CONTENT_RESTRICTED +
"set unrestricted_menu=\"y\"\n" \
"export unrestricted_menu\n"
FILE_CONTENT_WRONG = "#! /bin/sh\n" \
"exec tail -n +3 $0\n" \
"# File created by YaST and next YaST run probably overwrite it\n".freeze \
describe ".new" do
context "in first stage" do
before do
allow(Yast::Stage).to receive(:initial).and_return(true)
end
it "propose to not use password" do
expect(subject.used?).to eq false
end
it "propose to use unrestricted mode" do
expect(subject.unrestricted?).to eq true
end
it "do not have any password used" do
expect(subject.password?).to eq false
end
end
context "outside of first stage" do
before do
allow(Yast::Stage).to receive(:initial).and_return(false)
end
context "Grub password generator file do not exists" do
before do
Yast.import "FileUtils"
allow(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(false)
end
it "sets that password protection is not used" do
expect(subject.used?).to eq false
end
it "propose to use unrestricted mode" do
expect(subject.unrestricted?).to eq true
end
it "do not have any password used" do
expect(subject.password?).to eq false
end
end
context "Grub password generator file exists" do
before do
Yast.import "FileUtils"
allow(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(true)
allow(Yast::SCR).to receive(:Read)
.with(path(".target.string"), described_class::PWD_ENCRYPTION_FILE)
.and_return(FILE_CONTENT_RESTRICTED)
end
it "sets that password protection is used" do
expect(subject.used?).to eq true
end
it "sets that password is specified" do
expect(subject.password?).to eq true
end
it "sets restricted mode as is specified in file" do
allow(Yast::SCR).to receive(:Read)
.with(path(".target.string"), described_class::PWD_ENCRYPTION_FILE)
.and_return(FILE_CONTENT_RESTRICTED)
expect(described_class.new.unrestricted?).to eq false
allow(Yast::SCR).to receive(:Read)
.with(path(".target.string"), described_class::PWD_ENCRYPTION_FILE)
.and_return(FILE_CONTENT_UNRESTRICTED)
expect(described_class.new.unrestricted?).to eq true
end
it "raises exception if file content is not correct" do
allow(Yast::SCR).to receive(:Read)
.with(path(".target.string"), described_class::PWD_ENCRYPTION_FILE)
.and_return(FILE_CONTENT_WRONG)
expect { described_class.new }.to raise_error(RuntimeError)
end
end
end
end
describe "#write" do
context "password protection disabled" do
before do
subject.used = false
end
it "deletes Grub password generator file" do
Yast.import "FileUtils"
allow(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(true)
expect(Yast::SCR).to receive(:Execute)
.with(path(".target.bash"), "rm '#{described_class::PWD_ENCRYPTION_FILE}'")
subject.write
end
it "does nothing if Grub password generator file does not exist" do
Yast.import "FileUtils"
expect(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(false)
subject.write
end
end
context "password protection enabled" do
before do
subject.used = true
subject.unrestricted = false
# set directly encrypted password
subject.instance_variable_set(:@encrypted_password, ENCRYPTED_PASSWORD)
end
it "writes Grub password generator file" do
expect(Yast::SCR).to receive(:Write)
.with(
path(".target.string"),
[described_class::PWD_ENCRYPTION_FILE, 0o700],
FILE_CONTENT_RESTRICTED
)
subject.write
end
it "writes unrestricted generator if unrestricted variable set on" do
subject.unrestricted = true
expect(Yast::SCR).to receive(:Write)
.with(
path(".target.string"),
[described_class::PWD_ENCRYPTION_FILE, 0o700],
FILE_CONTENT_UNRESTRICTED
)
subject.write
end
it "writes restricted generator if unrestricted variable set off" do
subject.unrestricted = false
expect(Yast::SCR).to receive(:Write)
.with(
path(".target.string"),
[described_class::PWD_ENCRYPTION_FILE, 0o700],
FILE_CONTENT_RESTRICTED
)
subject.write
end
it "raises exception if password configuration is proposed and password not set" do
config = described_class.new
config.used = true
expect { config.write }.to raise_error(RuntimeError)
end
end
end
describe "#password=" do
it "sets encrypted version of given password" do
success_stdout = <<EOF
Enter password:
Reenter password:
PBKDF2 hash of your password is #{ENCRYPTED_PASSWORD}
EOF
expect(Yast::Execute).to receive(:locally)
.with(/grub2-mkpasswd/, anything)
.and_return(success_stdout)
subject.password = "really strong password"
expect(subject.instance_variable_get(:@encrypted_password)).to eq ENCRYPTED_PASSWORD
end
end
describe "#password?" do
it "returns false if password configuration is proposed from scratch" do
expect(subject.password?).to eq false
end
it "returns false if password is not enabled on disk" do
allow(Yast::Stage).to receive(:initial).and_return(false)
Yast.import "FileUtils"
allow(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(false)
expect(subject.password?).to eq false
end
it "returns true if password configuration exists on disk" do
allow(Yast::Stage).to receive(:initial).and_return(false)
Yast.import "FileUtils"
allow(Yast::FileUtils).to receive(:Exists)
.with(described_class::PWD_ENCRYPTION_FILE)
.and_return(true)
allow(Yast::SCR).to receive(:Read)
.with(path(".target.string"), described_class::PWD_ENCRYPTION_FILE)
.and_return(FILE_CONTENT_RESTRICTED)
expect(subject.password?).to eq true
end
it "returns true if password explicitly set" do
subject.instance_variable_set(:@encrypted_password, ENCRYPTED_PASSWORD)
expect(subject.password?).to eq true
end
end
end