]>
Commit | Line | Data |
---|---|---|
eee8cb5d SB |
1 | #!/usr/bin/env python3 |
2 | """ swtpm_setup.py | |
3 | ||
4 | A tool for simulating the manufacturing of a TPM 1.2 or 2.0 | |
5 | """ | |
6 | ||
7 | # Disable a couple of warnings: | |
8 | # 0912: Too many branches (15/12) (too-many-branches) | |
9 | # R0913: Too many arguments (6/5) (too-many-arguments) | |
10 | # R0914: Too many local variables (21/15) (too-many-locals) | |
11 | # R0101: Too many nested blocks (6/5) (too-many-nested-blocks) | |
12 | # W0703: Catching too general exception Exception (broad-except) | |
3064a72f SB |
13 | # C0302: Too many lines in module (1032/1000) (too-many-lines) |
14 | # pylint: disable=W0703,R0913,R0914,R0912,R0101,C0302 | |
eee8cb5d SB |
15 | |
16 | # | |
17 | # swtpm_setup.py | |
18 | # | |
19 | # Authors: Stefan Berger <stefanb@linux.ibm.com> | |
20 | # | |
21 | # (c) Copyright IBM Corporation 2020 | |
22 | # | |
23 | ||
24 | import datetime | |
25 | import distutils.spawn | |
26 | import getopt | |
27 | import getpass | |
28 | import glob | |
29 | import grp | |
30 | import json | |
31 | import os | |
3064a72f | 32 | import pwd |
eee8cb5d SB |
33 | import re |
34 | import subprocess | |
35 | import sys | |
36 | ||
37 | from py_swtpm_setup.swtpm_utils import logit, logerr, sha1 | |
38 | from py_swtpm_setup.swtpm_setup_conf import SWTPM_VER_MAJOR, SWTPM_VER_MINOR, \ | |
03c00c02 | 39 | SWTPM_VER_MICRO, SYSCONFDIR |
eee8cb5d SB |
40 | from py_swtpm_setup.swtpm import Swtpm2, Swtpm12 |
41 | ||
42 | # default values for passwords | |
43 | DEFAULT_OWNER_PASSWORD = "ooo" | |
44 | DEFAULT_SRK_PASSWORD = "sss" | |
45 | ||
46 | SETUP_CREATE_EK_F = 1 | |
47 | SETUP_TAKEOWN_F = 2 | |
48 | SETUP_EK_CERT_F = 4 | |
49 | SETUP_PLATFORM_CERT_F = 8 | |
50 | SETUP_LOCK_NVRAM_F = 16 | |
51 | SETUP_SRKPASS_ZEROS_F = 32 | |
52 | SETUP_OWNERPASS_ZEROS_F = 64 | |
53 | SETUP_STATE_OVERWRITE_F = 128 | |
54 | SETUP_STATE_NOT_OVERWRITE_F = 256 | |
55 | SETUP_TPM2_F = 512 | |
56 | SETUP_ALLOW_SIGNING_F = 1024 | |
57 | SETUP_TPM2_ECC_F = 2048 | |
58 | SETUP_CREATE_SPK_F = 4096 | |
59 | SETUP_DISPLAY_RESULTS_F = 8192 | |
60 | SETUP_DECRYPTION_F = 16384 | |
61 | ||
62 | # default configuration file | |
63 | SWTPM_SETUP_CONF = "swtpm_setup.conf" | |
64 | ||
65 | XCH = os.getenv('XDG_CONFIG_HOME') | |
66 | HOME = os.getenv('HOME') | |
67 | if XCH and os.access(os.path.join(XCH, SWTPM_SETUP_CONF), os.R_OK): | |
68 | DEFAULT_CONFIG_FILE = os.path.join(XCH, SWTPM_SETUP_CONF) | |
ecabc015 SB |
69 | elif HOME and os.access(os.path.join(HOME, ".config", SWTPM_SETUP_CONF), os.R_OK): |
70 | DEFAULT_CONFIG_FILE = os.path.join(HOME, ".config", SWTPM_SETUP_CONF) | |
eee8cb5d | 71 | else: |
03c00c02 | 72 | DEFAULT_CONFIG_FILE = os.path.join(os.sep + SYSCONFDIR, SWTPM_SETUP_CONF) |
eee8cb5d SB |
73 | |
74 | # default PCR banks to activate for TPM 2 | |
75 | DEFAULT_PCR_BANKS = "sha1,sha256" | |
76 | ||
77 | # Default logging goes to stderr | |
78 | LOGFILE = "" | |
79 | ||
80 | DEFAULT_RSA_KEYSIZE = 2048 | |
81 | ||
82 | ||
83 | def resolve_string(inp): | |
84 | """ resolve environment variables in a string """ | |
85 | result = "" | |
86 | sidx = 0 | |
87 | ||
88 | while True: | |
89 | idx = inp.find("${", sidx) | |
90 | if idx < 0: | |
91 | if sidx == 0: | |
92 | return inp | |
93 | result += inp[sidx:] | |
94 | return result | |
95 | ||
96 | result += inp[sidx:idx] | |
97 | eidx = inp.find("}", idx + 2) | |
98 | if eidx < 0: | |
99 | result += inp[idx:] | |
100 | return result | |
101 | ||
102 | result += os.getenv(inp[idx + 2:eidx], '') | |
103 | sidx = eidx + 1 | |
104 | ||
105 | ||
106 | def get_config_value(lines, configname): | |
107 | """ Get a config value from a list of strings """ | |
108 | regex = r'^' + configname + r"\s*=\s*([^#\n]*).*" | |
109 | for line in lines: | |
110 | match = re.match(regex, line) | |
111 | if match: | |
112 | return resolve_string(match.groups()[0]) | |
113 | return None | |
114 | ||
115 | ||
116 | def read_file(filename): | |
117 | """ read contents from a file """ | |
118 | try: | |
119 | fobj = open(filename, mode='rb') | |
120 | result = fobj.read() | |
121 | fobj.close() | |
122 | return result, 0 | |
123 | except Exception as err: | |
124 | logerr(LOGFILE, "Could not read from file %s: %s\n" % \ | |
125 | (filename, str(err))) | |
126 | return "", 1 | |
127 | ||
128 | ||
129 | def read_file_lines(filename): | |
130 | """ Read the lines from a file and return a list of the lines """ | |
131 | try: | |
132 | fobj = open(filename, 'r') | |
133 | lines = fobj.readlines() | |
134 | fobj.close() | |
135 | return lines, 0 | |
136 | except Exception as err: | |
137 | logerr(LOGFILE, "Could not access %s to get name of certificate tool " | |
138 | "to invoke: %s\n" % (filename, str(err))) | |
139 | return [], 1 | |
140 | ||
141 | ||
142 | def remove_file(filename): | |
143 | """ remove a file """ | |
144 | try: | |
145 | os.remove(filename) | |
146 | return 0 | |
147 | except Exception as err: | |
148 | logerr(LOGFILE, "Could not remove file %s: %s\n" % \ | |
149 | (filename, str(err))) | |
150 | return 1 | |
151 | ||
152 | ||
153 | def tpm_get_specs_and_attributes(swtpm): | |
154 | """ Get the TPM specification and attribute parameters """ | |
155 | ||
156 | res, ret = swtpm.ctrl_get_tpm_specs_and_attrs() | |
157 | if ret != 0: | |
158 | logerr(LOGFILE, "Could not get the TPM spec and attribute parameters.\n") | |
159 | return [], 1 | |
160 | ||
161 | res = res.replace(":00,", ":0,") # needed for libtpms <= 0.7.x | |
162 | try: | |
163 | tpm_param = json.loads(res) | |
164 | except json.decoder.JSONDecodeError as err: | |
165 | logerr(LOGFILE, "Internal error: Could not parse '%s' as JSON: %s\n" % | |
094dba93 | 166 | (res, str(err))) |
eee8cb5d SB |
167 | return [], 1 |
168 | ||
169 | params = ["--tpm-spec-family", tpm_param["TPMSpecification"]["family"], | |
170 | "--tpm-spec-level", str(tpm_param["TPMSpecification"]["level"]), | |
171 | "--tpm-spec-revision", str(tpm_param["TPMSpecification"]["revision"]), | |
172 | "--tpm-manufacturer", str(tpm_param["TPMAttributes"]["manufacturer"]), | |
173 | "--tpm-model", str(tpm_param["TPMAttributes"]["model"]), | |
174 | "--tpm-version", str(tpm_param["TPMAttributes"]["version"])] | |
175 | return params, 0 | |
176 | ||
177 | ||
178 | def call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): | |
179 | """ Call an external tool to create the certificates """ | |
180 | ||
181 | params, ret = tpm_get_specs_and_attributes(swtpm) | |
182 | if ret != 0: | |
183 | return 1 | |
184 | ||
185 | lines, ret = read_file_lines(config_file) | |
186 | if ret != 0: | |
187 | return ret | |
188 | ||
189 | create_certs_tool = get_config_value(lines, "create_certs_tool") | |
190 | create_certs_tool_config = get_config_value(lines, "create_certs_tool_config") | |
191 | create_certs_tool_options = get_config_value(lines, "create_certs_tool_options") | |
192 | ||
193 | ret = 0 | |
194 | ||
195 | if create_certs_tool: | |
196 | ret = 1 | |
197 | if flags & SETUP_TPM2_F: | |
198 | params.extend(["--tpm2"]) | |
199 | ||
200 | cmd = [create_certs_tool, | |
201 | "--type", "_", | |
202 | "--ek", ekparam, | |
203 | "--dir", certsdir] | |
204 | if len(LOGFILE) > 0: | |
205 | cmd.extend(["--logfile", LOGFILE]) | |
206 | if len(vmid) > 0: | |
207 | cmd.extend(["--vmid", vmid]) | |
208 | cmd.extend(params) | |
209 | if create_certs_tool_config: | |
210 | cmd.extend(["--configfile", create_certs_tool_config]) | |
211 | if create_certs_tool_options: | |
212 | cmd.extend(["--optsfile", create_certs_tool_options]) | |
213 | ||
214 | try: | |
215 | i = create_certs_tool.rindex(os.sep) | |
216 | prgname = create_certs_tool[i + 1:] | |
217 | except ValueError: | |
218 | prgname = create_certs_tool | |
219 | ||
220 | for entry in [(SETUP_EK_CERT_F, "ek"), (SETUP_PLATFORM_CERT_F, "platform")]: | |
221 | if flags & entry[0]: | |
222 | try: | |
223 | cmd[2] = entry[1] | |
224 | logit(LOGFILE, " Invoking %s\n" % (" ".join(cmd))) | |
225 | proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
a57c33a1 | 226 | stdout, _ = proc.communicate(timeout=30) |
eee8cb5d SB |
227 | for line in stdout.decode().split("\n"): |
228 | if len(line) > 0: | |
229 | logit(LOGFILE, "%s: %s\n" % (prgname, line)) | |
230 | ret = proc.returncode | |
231 | if ret != 0: | |
232 | logerr(LOGFILE, "Error returned from command\n") | |
233 | return 1 | |
234 | except FileNotFoundError as err: | |
235 | logerr(LOGFILE, "Could not execute %s: %s\n" % (create_certs_tool, str(err))) | |
236 | return 1 | |
237 | except subprocess.TimeoutExpired as err: | |
238 | logerr(LOGFILE, "%s did not finish in time: %s\n" % | |
239 | (create_certs_tool, str(err))) | |
240 | return 1 | |
241 | except Exception as err: | |
242 | logerr(LOGFILE, "An error occurred running %s: %s\n" % | |
243 | (create_certs_tool, str(err))) | |
244 | ||
245 | return ret | |
246 | ||
247 | ||
248 | def tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm): | |
249 | """ Create either an RSA or ECC EK and certificate """ | |
250 | ||
251 | if flags & SETUP_CREATE_EK_F: | |
252 | ekparam, ret = swtpm.create_ek(flags & SETUP_TPM2_ECC_F, rsa_keysize, | |
253 | flags & SETUP_ALLOW_SIGNING_F, flags & SETUP_DECRYPTION_F, | |
254 | flags & SETUP_LOCK_NVRAM_F) | |
255 | if ret != 0: | |
256 | return ret | |
257 | ||
258 | if flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F): | |
259 | ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) | |
260 | if ret != 0: | |
261 | return ret | |
262 | ||
263 | for entry in [(SETUP_EK_CERT_F, "ek.cert"), (SETUP_PLATFORM_CERT_F, "platform.cert")]: | |
264 | if flags & entry[0]: | |
265 | certfile = os.path.join(certsdir, entry[1]) | |
266 | data, ret = read_file(certfile) | |
267 | if ret != 0: | |
268 | logerr(LOGFILE, "%s file could not be read\n" % certfile) | |
269 | return ret | |
270 | if entry[0] == SETUP_EK_CERT_F: | |
271 | ret = swtpm.write_ek_cert_nvram(flags & SETUP_TPM2_ECC_F, rsa_keysize, | |
272 | flags & SETUP_LOCK_NVRAM_F, data) | |
273 | else: | |
274 | ret = swtpm.write_platform_cert_nvram(flags & SETUP_LOCK_NVRAM_F, data) | |
275 | remove_file(certfile) | |
276 | if ret != 0: | |
277 | return ret | |
278 | ||
279 | return 0 | |
280 | ||
281 | ||
282 | def tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm): | |
283 | """ Create RSA and ECC EKs and certificates """ | |
284 | ||
285 | # 1st key will be RSA | |
286 | flags = flags & ~SETUP_TPM2_ECC_F | |
287 | ret = tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm) | |
288 | if ret != 0: | |
289 | return 1 | |
290 | ||
291 | # 2nd key will be an ECC; no more platform cert | |
292 | flags = (flags & ~SETUP_PLATFORM_CERT_F) | SETUP_TPM2_ECC_F | |
293 | return tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm) | |
294 | ||
295 | ||
296 | def init_tpm2(flags, swtpm_prg_l, config_file, tpm2_state_path, vmid, pcr_banks, swtpm_keyopt, | |
297 | fds_to_pass, rsa_keysize): | |
298 | """ Initialize a TPM 2.0: create keys and certificate """ | |
299 | certsdir = tpm2_state_path | |
300 | ||
301 | swtpm = Swtpm2(swtpm_prg_l.copy(), tpm2_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) | |
302 | ||
303 | ret = swtpm.start() | |
304 | if ret != 0: | |
305 | logerr(LOGFILE, "Could not start the TPM 2.\n") | |
306 | return 1 | |
307 | ||
308 | if ret == 0: | |
309 | ret = swtpm.run_swtpm_bios() | |
310 | ||
311 | if ret == 0 and flags & SETUP_CREATE_SPK_F: | |
312 | ret = swtpm.create_spk(flags & SETUP_TPM2_ECC_F, rsa_keysize) | |
313 | ||
314 | if ret == 0: | |
315 | ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm) | |
316 | ||
317 | if ret == 0 and pcr_banks != "-": | |
318 | all_pcr_banks, ret = swtpm.get_all_pcr_banks() | |
319 | if ret == 0: | |
320 | active_pcr_banks, ret = swtpm.set_active_pcr_banks(pcr_banks.split(","), all_pcr_banks) | |
321 | if ret == 0: | |
322 | logit(LOGFILE, "Successfully activated PCR banks %s among %s.\n" % | |
323 | (",".join(active_pcr_banks), | |
324 | ",".join(all_pcr_banks))) | |
325 | ||
326 | if ret == 0: | |
327 | swtpm.shutdown() | |
328 | ||
329 | swtpm.destroy() | |
330 | ||
331 | return ret | |
332 | ||
333 | ||
334 | def tpm12_get_ownerpass_digest(flags, ownerpass): | |
335 | """ Get the owner password digest given the flags and possible owner password """ | |
336 | if not ownerpass: | |
337 | if flags & SETUP_OWNERPASS_ZEROS_F: | |
338 | ownerpass = ('\0' * 20) | |
339 | else: | |
340 | ownerpass = DEFAULT_OWNER_PASSWORD | |
341 | #print("Using owner password: %s\n" % ownerpass) | |
342 | return sha1(ownerpass.encode()) | |
343 | ||
344 | ||
345 | def tpm12_get_srkpass_digest(flags, srkpass): | |
346 | """ Get the SRK password digest given the flags and possible SRK password """ | |
347 | if not srkpass: | |
348 | if flags & SETUP_SRKPASS_ZEROS_F: | |
349 | srkpass = ('\0' * 20) | |
350 | else: | |
351 | srkpass = DEFAULT_SRK_PASSWORD | |
352 | #print("Using SRK password: %s\n" % srkpass) | |
353 | return sha1(srkpass.encode()) | |
354 | ||
355 | ||
356 | def tpm12_take_ownership(flags, ownerpass, srkpass, pubek, swtpm): | |
357 | """ Take ownership of the TPM 1.2; prepare the passwords """ | |
358 | ||
359 | return swtpm.take_ownership(tpm12_get_ownerpass_digest(flags, ownerpass), | |
360 | tpm12_get_srkpass_digest(flags, srkpass), pubek) | |
361 | ||
362 | ||
363 | def tpm12_ownerclear(flags, ownerpass, swtpm): | |
364 | """ Clear ownership of the TPM 1.2; prepare the password """ | |
365 | return swtpm.ownerclear(tpm12_get_ownerpass_digest(flags, ownerpass)) | |
366 | ||
367 | ||
368 | def tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): | |
369 | """ Create certificates for the TPM 1.2 and write them into NVRAM """ | |
370 | ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) | |
371 | if ret != 0: | |
372 | return 1 | |
373 | ||
374 | for entry in [(SETUP_EK_CERT_F, "ek.cert"), (SETUP_PLATFORM_CERT_F, "platform.cert")]: | |
375 | if flags & entry[0]: | |
376 | certfile = os.path.join(certsdir, entry[1]) | |
377 | data, ret = read_file(certfile) | |
378 | if ret != 0: | |
379 | logerr(LOGFILE, "%s file could not be read\n" % certfile) | |
380 | return 1 | |
381 | if entry[0] == SETUP_EK_CERT_F: | |
382 | ret = swtpm.write_ek_cert_nvram(data) | |
383 | if ret == 0: | |
384 | logit(LOGFILE, "Successfully created NVRAM area for EK certificate.\n") | |
385 | else: | |
386 | ret = swtpm.write_platform_cert_nvram(data) | |
387 | if ret == 0: | |
388 | logit(LOGFILE, "Successfully created NVRAM area for Platform certificate.\n") | |
389 | remove_file(certfile) | |
390 | if ret != 0: | |
391 | return 1 | |
392 | ||
393 | return 0 | |
394 | ||
395 | ||
396 | def init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, | |
397 | swtpm_keyopt, fds_to_pass): | |
398 | """ Initialize a TPM 1.2: create keys and certificate and take ownership """ | |
399 | certsdir = tpm_state_path | |
400 | ||
401 | swtpm = Swtpm12(swtpm_prg_l.copy(), tpm_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) | |
402 | ||
403 | ret = swtpm.start() | |
404 | if ret != 0: | |
405 | logerr(LOGFILE, "Could not start the TPM 2.\n") | |
406 | return 1 | |
407 | # We will have to call swtpm.destroy() at the end | |
408 | ||
409 | if ret == 0: | |
410 | ret = swtpm.run_swtpm_bios() | |
411 | ||
412 | if ret == 0 and flags & SETUP_CREATE_EK_F: | |
413 | pubek, ret = swtpm.create_endorsement_key_pair() | |
414 | if ret == 0: | |
415 | logit(LOGFILE, "Successfully created EK.\n") | |
416 | ||
417 | if ret == 0 and flags & SETUP_TAKEOWN_F: | |
418 | ret = tpm12_take_ownership(flags, ownerpass, srkpass, pubek, swtpm) | |
419 | if ret == 0: | |
420 | logit(LOGFILE, "Successfully took ownership of the TPM.\n") | |
421 | ||
422 | if ret == 0 and flags & SETUP_EK_CERT_F: | |
423 | ekparam = pubek.hex() | |
424 | ret = tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) | |
425 | ||
426 | if ret == 0 and flags & SETUP_LOCK_NVRAM_F: | |
427 | ret = swtpm.nv_lock() | |
428 | if ret == 0: | |
429 | logit(LOGFILE, "Successfully locked NVRAM access.\n") | |
430 | ||
431 | swtpm.destroy() | |
432 | ||
433 | return ret | |
434 | ||
435 | ||
436 | def check_state_overwrite(flags, tpm_state_path): | |
437 | """ Check whether we are allowed to overwrite existing state """ | |
438 | if flags & SETUP_TPM2_F: | |
439 | statefile = "tpm2-00.permall" | |
440 | else: | |
441 | statefile = "tpm-00.permall" | |
442 | ||
443 | if os.access(os.path.join(tpm_state_path, statefile), os.R_OK|os.W_OK): | |
444 | if flags & SETUP_STATE_NOT_OVERWRITE_F: | |
445 | logit(LOGFILE, "Not overwriting existing state file.\n") | |
446 | return 2 | |
447 | if flags & SETUP_STATE_OVERWRITE_F: | |
448 | return 0 | |
449 | logerr(LOGFILE, "Found existing TPM state file %s.\n" % statefile) | |
450 | return 1 | |
451 | ||
452 | return 0 | |
453 | ||
454 | ||
1d6a1554 SB |
455 | def delete_state(flags, tpm_state_path): |
456 | """ Delete the TPM's state file """ | |
457 | if flags & SETUP_TPM2_F: | |
458 | statefile = "tpm2-00.permall" | |
459 | else: | |
460 | statefile = "tpm-00.permall" | |
461 | ||
462 | filepath = os.path.join(tpm_state_path, statefile) | |
463 | try: | |
464 | os.unlink(os.path.join(tpm_state_path, statefile)) | |
465 | except Exception as err: | |
466 | logerr(LOGFILE, "Could not remove state file %s: %s\n" % \ | |
467 | (filepath, str(err))) | |
468 | ||
469 | ||
eee8cb5d SB |
470 | def versioninfo(): |
471 | """ Display version info """ | |
472 | print('TPM emulator setup tool version %d.%d.%d' % | |
473 | (SWTPM_VER_MAJOR, SWTPM_VER_MINOR, SWTPM_VER_MICRO)) | |
474 | ||
475 | ||
476 | def usage(prgname): | |
477 | """ Display versioninfo and usage """ | |
478 | versioninfo() | |
479 | print( | |
480 | "Usage: {prgname} [options]\n" | |
481 | "\n" | |
482 | "The following options are supported:\n" | |
483 | "\n" | |
484 | "--runas <user> : Use the given user id to switch to and run this program;\n" | |
485 | " this parameter is interpreted by swtpm_setup that switches\n" | |
486 | " to this user and invokes swtpm_setup.sh.\n" | |
487 | "\n" | |
488 | "--tpm-state <dir>: Path to a directory where the TPM's state will be written\n" | |
489 | " into; this is a mandatory argument\n" | |
490 | "\n" | |
491 | "--tpmstate <dir> : This is an alias for --tpm-state <dir>.\n" | |
492 | "\n" | |
493 | "--tpm <executable>\n" | |
494 | " : Path to the TPM executable; this is an optional argument and\n" | |
495 | " by default 'swtpm' in the PATH is used.\n" | |
496 | "\n" | |
497 | "--swtpm_ioctl <executable>\n" | |
498 | " : Path to the swtpm_ioctl executable; this is deprecated\n" | |
499 | " argument.\n" | |
500 | "\n" | |
501 | "--tpm2 : Setup a TPM 2; by default a TPM 1.2 is setup.\n" | |
502 | "\n" | |
503 | "--createek : Create the EK; for a TPM 2 an RSA and ECC EK will be\n" | |
504 | " created\n" | |
505 | "\n" | |
506 | "--allow-signing : Create an EK that can be used for signing;\n" | |
507 | " this option requires --tpm2.\n" | |
ef1407f5 | 508 | " Note: Careful, this option will create a non-standard EK!\n" |
eee8cb5d SB |
509 | "\n" |
510 | "--decryption : Create an EK that can be used for key encipherment;\n" | |
511 | " this is the default unless --allow-signing is given;\n" | |
512 | " this option requires --tpm2.\n" | |
513 | "\n" | |
514 | "--ecc : This option allows to create a TPM 2's ECC key as storage\n" | |
515 | " primary key; a TPM 2 always gets an RSA and an ECC EK key.\n" | |
516 | "\n" | |
517 | "--take-ownership : Take ownership; this option implies --createek\n" | |
518 | " --ownerpass <password>\n" | |
519 | " : Provide custom owner password; default is {DEFAULT_OWNER_PASSWORD}\n" | |
520 | " --owner-well-known:\n" | |
521 | " : Use an owner password of 20 zero bytes\n" | |
522 | " --srkpass <password>\n" | |
523 | " : Provide custom SRK password; default is {DEFAULT_SRK_PASSWORD}\n" | |
524 | " --srk-well-known:\n" | |
525 | " : Use an SRK password of 20 zero bytes\n" | |
526 | "--create-ek-cert : Create an EK certificate; this implies --createek\n" | |
527 | "\n" | |
528 | "--create-platform-cert\n" | |
529 | " : Create a platform certificate; this implies --create-ek-cert\n" | |
530 | "\n" | |
531 | "--create-spk : Create storage primary key; this requires --tpm2\n" | |
532 | "\n" | |
533 | "--lock-nvram : Lock NVRAM access\n" | |
534 | "\n" | |
535 | "--display : At the end display as much info as possible about the\n" | |
536 | " configuration of the TPM\n" | |
537 | "\n" | |
538 | "--config <config file>\n" | |
539 | " : Path to configuration file; default is {DEFAULT_CONFIG_FILE}\n" | |
540 | "\n" | |
541 | "--logfile <logfile>\n" | |
542 | " : Path to log file; default is logging to stderr\n" | |
543 | "\n" | |
544 | "--keyfile <keyfile>\n" | |
545 | " : Path to a key file containing the encryption key for the\n" | |
546 | " TPM to encrypt its persistent state with. The content\n" | |
547 | " must be a 32 hex digit number representing a 128bit AES key.\n" | |
548 | " This parameter will be passed to the TPM using\n" | |
549 | " '--key file=<file>'.\n" | |
550 | "\n" | |
551 | "--keyfile-fd <fd>: Like --keyfile but file descriptor is given to read\n" | |
552 | " encryption key from.\n" | |
553 | "\n" | |
554 | "--pwdfile <pwdfile>\n" | |
555 | " : Path to a file containing a passphrase from which the\n" | |
556 | " TPM will derive the 128bit AES key. The passphrase can be\n" | |
557 | " 32 bytes long.\n" | |
558 | " This parameter will be passed to the TPM using\n" | |
559 | " '--key pwdfile=<file>'.\n" | |
560 | "\n" | |
561 | "--pwdfile-fd <fd>: Like --pwdfile but file descriptor to read passphrase\n" | |
562 | " from is given.\n" | |
563 | "\n" | |
564 | "--cipher <cipher>: The cipher to use; either aes-128-cbc or aes-256-cbc;\n" | |
565 | " the default is aes-128-cbc; the same cipher must be\n" | |
566 | " used on the swtpm command line\n" | |
567 | "\n" | |
568 | "--overwrite : Overwrite existing TPM state be re-initializing it; if this\n" | |
569 | " option is not given, this program will return an error if\n" | |
570 | " existing state is detected\n" | |
571 | "\n" | |
572 | "--not-overwrite : Do not overwrite existing TPM state but silently end\n" | |
573 | "\n" | |
574 | "--pcr-banks <banks>\n" | |
575 | " : Set of PCR banks to activate. Provide a comma separated list\n" | |
576 | " like 'sha1,sha256'. '-' to skip and leave all banks active.\n" | |
577 | " Default: {DEFAULT_PCR_BANKS}\n" | |
578 | "\n" | |
579 | "--rsa-keysize <keysize>\n" | |
580 | " : The RSA key size of the EK key; 3072 bits may be supported\n" | |
581 | " if libtpms supports it.\n" | |
582 | " Default: {DEFAULT_RSA_KEYSIZE}\n" | |
583 | "\n" | |
584 | "--tcsd-system-ps-file <file>\n" | |
585 | " : This option is deprecated and has no effect.\n" | |
586 | "\n" | |
3f2bde08 SB |
587 | "--print-capabilities\n" |
588 | " : Print JSON formatted capabilites added after v0.1 and exit.\n" | |
589 | "\n" | |
eee8cb5d SB |
590 | "--version : Display version and exit\n" |
591 | "\n" | |
592 | "--help,-h,-? : Display this help screen".format_map({ | |
593 | 'prgname': prgname, | |
594 | 'DEFAULT_OWNER_PASSWORD': DEFAULT_OWNER_PASSWORD, | |
595 | 'DEFAULT_SRK_PASSWORD': DEFAULT_SRK_PASSWORD, | |
596 | 'DEFAULT_CONFIG_FILE': DEFAULT_CONFIG_FILE, | |
597 | 'DEFAULT_PCR_BANKS': DEFAULT_PCR_BANKS, | |
598 | 'DEFAULT_RSA_KEYSIZE': DEFAULT_RSA_KEYSIZE | |
599 | })) | |
600 | ||
601 | ||
602 | def get_rsa_keysizes(flags, swtpm_prg_l): | |
603 | """ Get the support RSA key sizes | |
604 | @return This function returns a list of ints like the following | |
605 | - [ 1024, 2048, 3072 ] | |
606 | - [] (empty list, indicating only 2048 bit RSA keys are supported) | |
607 | """ | |
608 | res = [] | |
609 | ||
610 | if flags & SETUP_TPM2_F: | |
611 | cmdarray = swtpm_prg_l.copy() | |
612 | cmdarray.extend(["--tpm2", "--print-capabilities"]) | |
613 | try: | |
614 | process = subprocess.Popen(cmdarray, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
615 | stdout, _ = process.communicate() | |
616 | try: | |
617 | instr = stdout.decode() | |
618 | j = json.loads(instr) | |
619 | for entry in j["features"]: | |
620 | if entry.startswith("rsa-keysize-"): | |
621 | try: | |
622 | res.append(int(entry[12:])) | |
623 | except ValueError as err: | |
624 | logerr(LOGFILE, "Internal error: Could not parse '%s' as int: %s\n" % | |
625 | (entry[12:], str(err))) | |
626 | return [], 1 | |
627 | except json.decoder.JSONDecodeError as err: | |
628 | logerr(LOGFILE, "Internal error: Could not parse '%s' as JSON: %s\n" % | |
629 | (instr, str(err))) | |
630 | return [], 1 | |
631 | except Exception as err: | |
632 | logerr(LOGFILE, "Could not start swtpm '%s': %s\n" % (" ".join(swtpm_prg_l), str(err))) | |
633 | return res, 1 | |
634 | ||
635 | return res, 0 | |
636 | ||
637 | ||
638 | def get_rsakeysize_caps(flags, swtpm_prg_l): | |
639 | """ Get supported RSA key sizes useful for creating the EK key; only 2048 | |
640 | and 3072 bits are checked and reported. | |
641 | This function returns a list of strings like the following | |
642 | - [ "tpm2-rsa-keysize-2048", "tpm2-rsa-keyssize-3072" ] | |
643 | - [] (empty list, indicating only 2048 bit RSA keys are supported) | |
644 | """ | |
645 | ||
646 | supt_keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) | |
647 | if ret != 0: | |
648 | return [], 1 | |
649 | ||
650 | res = [] | |
651 | for keysize in supt_keysizes: | |
652 | if keysize >= 2048: | |
653 | res.append("tpm2-rsa-keysize-%d" % keysize) | |
654 | ||
655 | return res, 0 | |
656 | ||
657 | ||
658 | def print_capabilities(swtpm_prg_l): | |
659 | """ pring JSON string with capabilites """ | |
660 | param = "" | |
661 | ||
662 | output, ret = get_rsakeysize_caps(SETUP_TPM2_F, swtpm_prg_l) | |
663 | if ret != 0: | |
664 | return 1 | |
665 | if len(output) > 0: | |
666 | param = ', "' + '", "'.join(output) + '"' | |
667 | ||
668 | print('{ "type": "swtpm_setup", ' \ | |
669 | '"features": [ "cmdarg-keyfile-fd", "cmdarg-pwdfile-fd", "tpm12-not-need-root"' + | |
670 | param + ' ] '\ | |
671 | '}') | |
672 | ||
673 | return 0 | |
674 | ||
3064a72f SB |
675 | def change_process_owner(user): |
676 | """ change the process owner to the given one """ | |
677 | if not user.isnumeric(): | |
678 | try: | |
679 | passwd = pwd.getpwnam(user) | |
680 | except KeyError: | |
681 | logerr(LOGFILE, "Error: User '%s' does not exist.\n" % user) | |
682 | return 1 | |
683 | ||
684 | try: | |
685 | os.initgroups(passwd.pw_name, passwd.pw_gid) | |
686 | except PermissionError as err: | |
687 | logerr(LOGFILE, "Error: initgroups() failed: %s\n" % str(err)) | |
688 | return 1 | |
689 | gid = passwd.pw_gid | |
690 | uid = passwd.pw_uid | |
691 | else: | |
692 | if int(user) > 0xffffffff: | |
693 | logerr(LOGFILE, "Error: uid %s outside valid range.\n" % user) | |
694 | gid = int(user) | |
695 | uid = int(user) | |
696 | ||
697 | try: | |
698 | os.setgid(gid) | |
699 | except PermissionError as err: | |
700 | logerr(LOGFILE, "Error: setgid(%d) failed: %s\n" % (gid, str(err))) | |
701 | return 1 | |
702 | ||
703 | try: | |
704 | os.setuid(uid) | |
705 | except PermissionError as err: | |
706 | logerr(LOGFILE, "Error: setuid(%d) failed: %s\n" % (uid, str(err))) | |
707 | return 1 | |
708 | ||
709 | return 0 | |
eee8cb5d SB |
710 | |
711 | # pylint: disable=R0915 | |
712 | def main(): | |
713 | """ main function - parses command line parameters and low level dealing with them """ | |
714 | global LOGFILE # pylint: disable=W0603 | |
715 | ||
716 | swtpm_prg = distutils.spawn.find_executable("swtpm") | |
717 | if swtpm_prg: | |
718 | swtpm_prg += " socket" | |
719 | ||
720 | try: | |
31ac7288 | 721 | opts, _ = getopt.getopt(sys.argv[1:], "h?", |
eee8cb5d SB |
722 | ["tpm-state=", "tpmstate=", |
723 | "tpm=", | |
724 | "swtpm_ioctl=", | |
725 | "tpm2", | |
726 | "ecc", | |
727 | "createek", | |
728 | "create-spk", | |
729 | "take-ownership", | |
730 | "ownerpass=", | |
731 | "owner-well-known", | |
732 | "srkpass=", | |
733 | "srk-well-known", | |
734 | "create-ek-cert", | |
735 | "create-platform-cert", | |
736 | "lock-nvram", | |
737 | "display", | |
738 | "config=", | |
739 | "vmid=", | |
740 | "keyfile=", | |
741 | "keyfile-fd=", | |
742 | "pwdfile=", | |
743 | "pwdfile-fd=", | |
744 | "cipher=", | |
745 | "runas=", | |
746 | "logfile=", | |
747 | "overwrite", | |
748 | "not-overwrite", | |
749 | "allow-signing", | |
750 | "decryption", | |
751 | "pcr-banks=", | |
752 | "rsa-keysize=", | |
753 | "tcsd-system-ps-file", | |
754 | "version", | |
755 | "print-capabilities", | |
31ac7288 | 756 | "help"]) |
eee8cb5d SB |
757 | except getopt.GetoptError as err: |
758 | print(err) | |
759 | usage(sys.argv[0]) | |
760 | sys.exit(1) | |
761 | ||
762 | flags = 0 | |
763 | tpm_state_path = "" | |
764 | config_file = DEFAULT_CONFIG_FILE | |
765 | ownerpass = None | |
766 | got_ownerpass = False | |
767 | srkpass = None | |
768 | got_srkpass = False | |
769 | vmid = "" | |
770 | pcr_banks = "" | |
771 | printcapabilities = False | |
772 | keyfile = "" | |
773 | keyfile_fd = "" | |
774 | pwdfile = "" | |
775 | pwdfile_fd = "" | |
776 | cipher = "aes-128-cbc" | |
777 | rsa_keysize_str = "%d" % DEFAULT_RSA_KEYSIZE | |
778 | swtpm_keyopt = "" | |
779 | fds_to_pass = [] | |
3064a72f | 780 | runas = "" |
eee8cb5d SB |
781 | |
782 | for opt, arg in opts: | |
783 | if opt in ['--tpm-state', '--tpmstate']: | |
784 | tpm_state_path = arg | |
785 | elif opt == '--tpm': | |
786 | swtpm_prg = arg | |
787 | elif opt == '--swtpm_ioctl': | |
788 | print("Warning: --swtpm_ioctl is deprecated and has no effect.") | |
789 | elif opt == '--tpm2': | |
790 | flags |= SETUP_TPM2_F | |
791 | elif opt == '--ecc': | |
792 | flags |= SETUP_TPM2_ECC_F | |
793 | elif opt == '--createek': | |
794 | flags |= SETUP_CREATE_EK_F | |
795 | elif opt == '--create-spk': | |
796 | flags |= SETUP_CREATE_SPK_F | |
797 | elif opt == '--take-ownership': | |
798 | flags |= SETUP_CREATE_EK_F | SETUP_TAKEOWN_F | |
799 | elif opt == '--ownerpass': | |
800 | ownerpass = arg | |
801 | got_ownerpass = True | |
802 | elif opt == '--owner-well-known': | |
803 | flags |= SETUP_OWNERPASS_ZEROS_F | |
804 | elif opt == '--srkpass': | |
805 | srkpass = arg | |
806 | got_srkpass = True | |
807 | elif opt == '--srk-well-known': | |
808 | flags |= SETUP_SRKPASS_ZEROS_F | |
809 | elif opt == '--create-ek-cert': | |
810 | flags |= SETUP_CREATE_EK_F | SETUP_EK_CERT_F | |
811 | elif opt == '--create-platform-cert': | |
812 | flags |= SETUP_CREATE_EK_F | SETUP_PLATFORM_CERT_F | |
813 | elif opt == '--lock-nvram': | |
814 | flags |= SETUP_LOCK_NVRAM_F | |
815 | elif opt == '--display': | |
816 | flags |= SETUP_DISPLAY_RESULTS_F | |
817 | elif opt == '--config': | |
818 | config_file = arg | |
819 | elif opt == '--vmid': | |
820 | vmid = arg | |
821 | elif opt == '--keyfile': | |
822 | keyfile = arg | |
823 | elif opt == '--keyfile-fd': | |
824 | keyfile_fd = arg | |
825 | elif opt == '--pwdfile': | |
826 | pwdfile = arg | |
827 | elif opt == '--pwdfile-fd': | |
828 | pwdfile_fd = arg | |
829 | elif opt == '--cipher': | |
830 | cipher = arg | |
831 | elif opt == '--runas': | |
3064a72f | 832 | runas = arg |
eee8cb5d SB |
833 | elif opt == '--logfile': |
834 | LOGFILE = arg | |
835 | elif opt == '--overwrite': | |
836 | flags |= SETUP_STATE_OVERWRITE_F | |
837 | elif opt == '--not-overwrite': | |
838 | flags |= SETUP_STATE_NOT_OVERWRITE_F | |
839 | elif opt == '--allow-signing': | |
840 | flags |= SETUP_ALLOW_SIGNING_F | |
841 | elif opt == '--decryption': | |
842 | flags |= SETUP_DECRYPTION_F | |
843 | elif opt == '--pcr-banks': | |
844 | pcr_banks = pcr_banks + "," + arg | |
845 | elif opt == '--rsa-keysize': | |
846 | rsa_keysize_str = arg | |
847 | elif opt == '--tcsd-system-ps-file': | |
848 | print("Warning: --tcsd-system-ps-file is deprecated and has no effect.") | |
849 | elif opt == '--version': | |
850 | versioninfo() | |
851 | sys.exit(0) | |
852 | elif opt == '--print-capabilities': | |
853 | printcapabilities = True | |
854 | elif opt in ['--help', '-h', '-?']: | |
855 | usage(sys.argv[0]) | |
856 | sys.exit(0) | |
857 | else: | |
858 | sys.stderr.write("Unknown option %s\n" % opt) | |
859 | usage(sys.argv[0]) | |
860 | sys.exit(1) | |
861 | ||
862 | if not swtpm_prg: | |
863 | logerr(LOGFILE, | |
864 | "Default TPM 'swtpm' could not be found and was not provided using --tpm\n.") | |
865 | sys.exit(1) | |
866 | swtpm_prg_l = swtpm_prg.split() | |
867 | if not distutils.spawn.find_executable(swtpm_prg_l[0]): | |
868 | logerr(LOGFILE, "TPM at %s is not an executable.\n" % (" ".join(swtpm_prg_l))) | |
869 | sys.exit(1) | |
870 | ||
871 | if printcapabilities: | |
872 | ret = print_capabilities(swtpm_prg_l) | |
873 | sys.exit(ret) | |
874 | ||
3064a72f SB |
875 | if runas: |
876 | ret = change_process_owner(runas) | |
877 | if ret != 0: | |
878 | sys.exit(1) | |
879 | ||
eee8cb5d SB |
880 | if not got_ownerpass: |
881 | flags |= SETUP_OWNERPASS_ZEROS_F | |
882 | if not got_srkpass: | |
883 | flags |= SETUP_SRKPASS_ZEROS_F | |
884 | ||
885 | # sequeeze ',' and remove leading and trailing ',' | |
886 | pcr_banks = re.sub(r',$', '', | |
887 | re.sub(r'^,', '', | |
888 | re.sub(r',,+', ',', pcr_banks))) | |
889 | if len(pcr_banks) == 0: | |
890 | pcr_banks = DEFAULT_PCR_BANKS | |
891 | ||
892 | # set owner password to default if user didn't provide any password wish | |
893 | # and wants to take ownership | |
894 | if flags & SETUP_TAKEOWN_F and \ | |
895 | flags & SETUP_OWNERPASS_ZEROS_F and \ | |
896 | not got_srkpass: | |
897 | srkpass = DEFAULT_SRK_PASSWORD | |
898 | ||
899 | if len(LOGFILE) > 0: | |
1efe61a6 SB |
900 | if os.path.islink(LOGFILE): |
901 | sys.stderr.write("Logfile must not be a symlink.\n") | |
902 | sys.exit(1) | |
eee8cb5d | 903 | try: |
2ba22483 | 904 | fobj = open(LOGFILE, "a") # do not truncate |
eee8cb5d SB |
905 | fobj.close() |
906 | except PermissionError: | |
907 | sys.stderr.write("Cannot write to logfile %s.\n", LOGFILE) | |
908 | sys.exit(1) | |
909 | ||
910 | # Check tpm_state_path directory and access rights | |
911 | if len(tpm_state_path) == 0: | |
912 | logerr(LOGFILE, "--tpm-state must be provided\n") | |
913 | sys.exit(1) | |
914 | if not os.path.isdir(tpm_state_path): | |
915 | logerr(LOGFILE, | |
916 | "User %s cannot access directory %s. Make sure it exists and is a directory.\n" % | |
917 | (getpass.getuser(), tpm_state_path)) | |
918 | sys.exit(1) | |
919 | if not os.access(tpm_state_path, os.R_OK): | |
920 | logerr(LOGFILE, "Need read rights on directory %s for user %s.\n" % | |
921 | (tpm_state_path, getpass.getuser())) | |
922 | sys.exit(1) | |
923 | if not os.access(tpm_state_path, os.W_OK): | |
924 | logerr(LOGFILE, "Need write rights on directory %s for user %s.\n" % | |
925 | (tpm_state_path, getpass.getuser())) | |
926 | sys.exit(1) | |
927 | ||
928 | if flags & SETUP_TPM2_F: | |
929 | if flags & SETUP_TAKEOWN_F: | |
930 | logerr(LOGFILE, "Taking ownership is not supported for TPM 2.\n") | |
931 | sys.exit(1) | |
932 | else: | |
933 | if flags & SETUP_TPM2_ECC_F: | |
934 | logerr(LOGFILE, "--ecc requires --tpm2.\n") | |
935 | sys.exit(1) | |
936 | if flags & SETUP_CREATE_SPK_F: | |
937 | logerr(LOGFILE, "--create-spk requires --tpm2.\n") | |
938 | sys.exit(1) | |
939 | ||
940 | ret = check_state_overwrite(flags, tpm_state_path) | |
941 | if ret == 1: | |
942 | sys.exit(1) | |
943 | elif ret == 2: | |
944 | sys.exit(0) | |
945 | ||
946 | files = glob.glob(os.path.join(tpm_state_path, "*permall")) | |
947 | files.extend(glob.glob(os.path.join(tpm_state_path, "*volatilestate"))) | |
948 | files.extend(glob.glob(os.path.join(tpm_state_path, "*savestate"))) | |
949 | ||
950 | try: | |
951 | for fil in files: | |
952 | os.remove(fil) | |
953 | except Exception as err: | |
954 | logerr(LOGFILE, "Could not remove previous state files. Need execute access rights on the " | |
955 | "directory %s.\n" % tpm_state_path) | |
956 | sys.exit(1) | |
957 | ||
958 | lockfile = os.path.join(tpm_state_path, ".lock") | |
959 | if os.path.exists(lockfile) and not os.access(lockfile, os.W_OK | os.R_OK): | |
960 | logerr(LOGFILE, "User %s cannot read/write %s.\n" % (getpass.getuser(), lockfile)) | |
961 | sys.exit(1) | |
962 | ||
963 | if not os.access(config_file, os.R_OK): | |
964 | logerr(LOGFILE, "User %s cannot read %s.\n" % (getpass.getuser(), config_file)) | |
965 | sys.exit(1) | |
966 | ||
967 | if len(cipher) > 0: | |
968 | if cipher not in ['aes-128-cbc', 'aes-cbc', 'aes-256-cbc']: | |
969 | logerr(LOGFILE, "Unsupported cipher %s.\n" % cipher) | |
970 | sys.exit(1) | |
971 | cipher = ",mode=%s" % cipher | |
972 | ||
973 | if len(keyfile) > 0: | |
974 | if not os.access(keyfile, os.R_OK): | |
975 | logerr(LOGFILE, "User %s cannot read keyfile %s.\n" % (getpass.getuser(), keyfile)) | |
976 | sys.exit(1) | |
977 | swtpm_keyopt = "file=%s%s" % (keyfile, cipher) | |
978 | logit(LOGFILE, " The TPM's state will be encrypted with a provided key.\n") | |
979 | elif len(pwdfile) > 0: | |
980 | if not os.access(pwdfile, os.R_OK): | |
981 | logerr(LOGFILE, "User %s canot read passphrase file %s.\n" % \ | |
982 | (getpass.getuser(), keyfile)) | |
983 | sys.exit(1) | |
984 | swtpm_keyopt = "pwdfile=%s%s" % (pwdfile, cipher) | |
985 | logit(LOGFILE, | |
986 | " The TPM's state will be encrypted using a key derived from a passphrase.\n") | |
987 | elif len(keyfile_fd) > 0: | |
988 | if not keyfile_fd.isnumeric(): | |
989 | logerr(LOGFILE, | |
990 | "--keyfile-fd parameter $keyfile_fd is not a valid file descriptor.\n") | |
991 | sys.exit(1) | |
992 | fds_to_pass.append(int(keyfile_fd)) | |
993 | swtpm_keyopt = "fd=%s%s" % (keyfile_fd, cipher) | |
994 | logit(LOGFILE, | |
995 | " The TPM's state will be encrypted with a provided key (fd).\n") | |
996 | elif len(pwdfile_fd) > 0: | |
997 | if not pwdfile_fd.isnumeric(): | |
998 | logerr(LOGFILE, | |
999 | "--pwdfile-fd parameter $pwdfile_fd is not a valid file descriptor.\n") | |
1000 | sys.exit(1) | |
1001 | fds_to_pass.append(int(pwdfile_fd)) | |
1002 | swtpm_keyopt = "pwdfd=%s%s" % (pwdfile_fd, cipher) | |
1003 | logit(LOGFILE, | |
1004 | " The TPM's state will be encrypted using a key derived from a passphrase (fd).\n") | |
1005 | ||
1006 | if rsa_keysize_str == "max": | |
1007 | keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) | |
1008 | if ret != 0: | |
1009 | sys.exit(1) | |
1010 | if len(keysizes) > 0: | |
1011 | rsa_keysize_str = keysizes[-1] | |
1012 | else: | |
1013 | rsa_keysize_str = "2048" | |
1014 | if rsa_keysize_str in ["2048", "3072"]: | |
1015 | rsa_keysize = int(rsa_keysize_str) | |
1016 | supported_keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) | |
1017 | if ret != 0: | |
1018 | sys.exit(1) | |
1019 | if rsa_keysize not in supported_keysizes and rsa_keysize != 2048: | |
1020 | logerr(LOGFILE, "%s bit RSA keys are not supported by libtpms.\n" % rsa_keysize_str) | |
1021 | sys.exit(1) | |
1022 | else: | |
1023 | logerr(LOGFILE, "Unsupported RSA key size %s.\n" % rsa_keysize_str) | |
1024 | sys.exit(1) | |
1025 | ||
1026 | user = getpass.getuser() | |
1027 | try: | |
1028 | group = grp.getgrnam(user)[0] | |
1029 | except KeyError: | |
1030 | group = "<unknown>" | |
1031 | tzinfo = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo | |
1032 | logit(LOGFILE, "Starting vTPM manufacturing as %s:%s @ %s\n" % | |
1033 | (user, group, | |
1034 | datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z"))) | |
1035 | ||
1036 | if not flags & SETUP_TPM2_F: | |
1037 | ret = init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, | |
1038 | swtpm_keyopt, fds_to_pass) | |
1039 | else: | |
1040 | ret = init_tpm2(flags, swtpm_prg_l, config_file, tpm_state_path, vmid, pcr_banks, | |
1041 | swtpm_keyopt, fds_to_pass, rsa_keysize) | |
1042 | ||
1043 | if ret == 0: | |
1044 | logit(LOGFILE, "Successfully authored TPM state.\n") | |
1045 | else: | |
1046 | logerr(LOGFILE, "An error occurred. Authoring the TPM state failed.\n") | |
1d6a1554 | 1047 | delete_state(flags, tpm_state_path) |
eee8cb5d SB |
1048 | |
1049 | logit(LOGFILE, "Ending vTPM manufacturing @ %s\n" % | |
1050 | datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z")) | |
1051 | ||
1052 | sys.exit(ret) | |
1053 | ||
1054 | ||
1055 | if __name__ == "__main__": | |
1056 | main() |