]> git.proxmox.com Git - swtpm.git/blame - src/swtpm_setup/py_swtpm_setup/swtpm_setup.py
swtpm_setup: Write note about non-standard EK when using --allow-signing
[swtpm.git] / src / swtpm_setup / py_swtpm_setup / swtpm_setup.py
CommitLineData
eee8cb5d
SB
1#!/usr/bin/env python3
2""" swtpm_setup.py
3
4A 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
24import datetime
25import distutils.spawn
26import getopt
27import getpass
28import glob
29import grp
30import json
31import os
3064a72f 32import pwd
eee8cb5d
SB
33import re
34import subprocess
35import sys
36
37from py_swtpm_setup.swtpm_utils import logit, logerr, sha1
38from py_swtpm_setup.swtpm_setup_conf import SWTPM_VER_MAJOR, SWTPM_VER_MINOR, \
03c00c02 39 SWTPM_VER_MICRO, SYSCONFDIR
eee8cb5d
SB
40from py_swtpm_setup.swtpm import Swtpm2, Swtpm12
41
42# default values for passwords
43DEFAULT_OWNER_PASSWORD = "ooo"
44DEFAULT_SRK_PASSWORD = "sss"
45
46SETUP_CREATE_EK_F = 1
47SETUP_TAKEOWN_F = 2
48SETUP_EK_CERT_F = 4
49SETUP_PLATFORM_CERT_F = 8
50SETUP_LOCK_NVRAM_F = 16
51SETUP_SRKPASS_ZEROS_F = 32
52SETUP_OWNERPASS_ZEROS_F = 64
53SETUP_STATE_OVERWRITE_F = 128
54SETUP_STATE_NOT_OVERWRITE_F = 256
55SETUP_TPM2_F = 512
56SETUP_ALLOW_SIGNING_F = 1024
57SETUP_TPM2_ECC_F = 2048
58SETUP_CREATE_SPK_F = 4096
59SETUP_DISPLAY_RESULTS_F = 8192
60SETUP_DECRYPTION_F = 16384
61
62# default configuration file
63SWTPM_SETUP_CONF = "swtpm_setup.conf"
64
65XCH = os.getenv('XDG_CONFIG_HOME')
66HOME = os.getenv('HOME')
67if 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
69elif 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 71else:
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
75DEFAULT_PCR_BANKS = "sha1,sha256"
76
77# Default logging goes to stderr
78LOGFILE = ""
79
80DEFAULT_RSA_KEYSIZE = 2048
81
82
83def 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
106def 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
116def 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
129def 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
142def 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
153def 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
178def 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
248def 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
282def 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
296def 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
334def 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
345def 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
356def 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
363def 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
368def 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
396def 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
436def 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
455def 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
470def 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
476def 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
602def 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
638def 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
658def 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
675def 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
712def 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
1055if __name__ == "__main__":
1056 main()