def change_process_owner(user): """ change the process owner to the given one """ if not user.isnumeric(): try: passwd = pwd.getpwnam(user) except KeyError: logerr(LOGFILE, "Error: User '%s' does not exist.\n" % user) return 1 try: os.initgroups(passwd.pw_name, passwd.pw_gid) except PermissionError as err: logerr(LOGFILE, "Error: initgroups() failed: %s\n" % str(err)) return 1 gid = passwd.pw_gid uid = passwd.pw_uid else: if int(user) > 0xffffffff: logerr(LOGFILE, "Error: uid %s outside valid range.\n" % user) gid = int(user) uid = int(user) try: os.setgid(gid) except PermissionError as err: logerr(LOGFILE, "Error: setgid(%d) failed: %s\n" % (gid, str(err))) return 1 try: os.setuid(uid) except PermissionError as err: logerr(LOGFILE, "Error: setuid(%d) failed: %s\n" % (uid, str(err))) return 1 return 0
def tpm_get_specs_and_attributes(swtpm): """ Get the TPM specification and attribute parameters """ res, ret = swtpm.ctrl_get_tpm_specs_and_attrs() if ret != 0: logerr(LOGFILE, "Could not get the TPM spec and attribute parameters.\n") return [], 1 res = res.replace(":00,", ":0,") # needed for libtpms <= 0.7.x try: tpm_param = json.loads(res) except json.decoder.JSONDecodeError as err: logerr( LOGFILE, "Internal error: Could not parse '%s' as JSON: %s\n" % (res, str(err))) return [], 1 params = [ "--tpm-spec-family", tpm_param["TPMSpecification"]["family"], "--tpm-spec-level", str(tpm_param["TPMSpecification"]["level"]), "--tpm-spec-revision", str(tpm_param["TPMSpecification"]["revision"]), "--tpm-manufacturer", str(tpm_param["TPMAttributes"]["manufacturer"]), "--tpm-model", str(tpm_param["TPMAttributes"]["model"]), "--tpm-version", str(tpm_param["TPMAttributes"]["version"]) ] return params, 0
def tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm): """ Create either an RSA or ECC EK and certificate """ if flags & SETUP_CREATE_EK_F: ekparam, ret = swtpm.create_ek(flags & SETUP_TPM2_ECC_F, rsa_keysize, flags & SETUP_ALLOW_SIGNING_F, flags & SETUP_DECRYPTION_F, flags & SETUP_LOCK_NVRAM_F) if ret != 0: return ret if flags & (SETUP_EK_CERT_F | SETUP_PLATFORM_CERT_F): ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) if ret != 0: return ret for entry in [(SETUP_EK_CERT_F, "ek.cert"), (SETUP_PLATFORM_CERT_F, "platform.cert")]: if flags & entry[0]: certfile = os.path.join(certsdir, entry[1]) data, ret = read_file(certfile) if ret != 0: logerr(LOGFILE, "%s file could not be read\n" % certfile) return ret if entry[0] == SETUP_EK_CERT_F: ret = swtpm.write_ek_cert_nvram(flags & SETUP_TPM2_ECC_F, rsa_keysize, flags & SETUP_LOCK_NVRAM_F, data) else: ret = swtpm.write_platform_cert_nvram(flags & SETUP_LOCK_NVRAM_F, data) remove_file(certfile) if ret != 0: return ret return 0
def tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): """ Create certificates for the TPM 1.2 and write them into NVRAM """ ret = call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) if ret != 0: return 1 for entry in [(SETUP_EK_CERT_F, "ek.cert"), (SETUP_PLATFORM_CERT_F, "platform.cert")]: if flags & entry[0]: certfile = os.path.join(certsdir, entry[1]) data, ret = read_file(certfile) if ret != 0: logerr(LOGFILE, "%s file could not be read\n" % certfile) return 1 if entry[0] == SETUP_EK_CERT_F: ret = swtpm.write_ek_cert_nvram(data) if ret == 0: logit(LOGFILE, "Successfully created NVRAM area for EK certificate.\n") else: ret = swtpm.write_platform_cert_nvram(data) if ret == 0: logit(LOGFILE, "Successfully created NVRAM area for Platform certificate.\n") remove_file(certfile) if ret != 0: return 1 return 0
def write_ek_cert_nvram(self, isecc, rsa_keysize, lock_nvram, ekcert): """ Write the given ekcert into an NVRAM area appropriate for the key type and size """ if not isecc: if rsa_keysize == 2048: nvindex = TPM2_NV_INDEX_RSA2048_EKCERT elif rsa_keysize == 3072: nvindex = TPM2_NV_INDEX_RSA3072_HI_EKCERT keytype = "RSA %d" % rsa_keysize else: nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKCERT keytype = "ECC" nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, ekcert, lock_nvram, "EK Certificate") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for %s EK certificate.\n" % (nvindex, keytype)) else: logerr( self.logfile, "Could not create NVRAM area 0x%x for %s EK certificate.\n" % (nvindex, keytype)) return ret
def remove_file(filename): """ remove a file """ try: os.remove(filename) return 0 except Exception as err: logerr(LOGFILE, "Could not remove file %s: %s\n" % \ (filename, str(err))) return 1
def set_active_pcr_banks(self, pcr_banks, all_pcr_banks): """ Set the list of active PCR banks to the one the user wants """ pcrselects = "".encode() count = 0 active = [] # enable the ones the user wants for pcr_bank in pcr_banks: if pcr_bank not in all_pcr_banks: # Skip if not even available continue try: hashalg = BANK_NAMES_TO_ALGID[pcr_bank] except KeyError: continue active.insert(0, pcr_bank) pcrselects += struct.pack('>H BBBB', hashalg, 3, 0xff, 0xff, 0xff) #print("activate hashalg = %d\n" % hashalg) count += 1 if len(active) == 0: logerr( self.logfile, "No PCR banks could be allocated. None of the selected algorithms are " "supported.\n") return [], 1 # disable the rest for pcr_bank in all_pcr_banks: if pcr_bank in pcr_banks: # Skip if to activate continue try: hashalg = BANK_NAMES_TO_ALGID[pcr_bank] except KeyError: continue #print("deactivate hashalg = %d\n" % hashalg) pcrselects += struct.pack('>H BBBB', hashalg, 3, 0, 0, 0) count += 1 authblock = struct.pack('>I HBH', TPM2_RS_PW, 0, 0, 0) fmt = '>HII I I%ds I %ds' % (len(authblock), len(pcrselects)) req = struct.pack(fmt, TPM2_ST_SESSIONS, struct.calcsize(fmt), TPM2_CC_PCR_ALLOCATE, TPM2_RH_PLATFORM, len(authblock), authblock, count, pcrselects) _, ret = self.transfer(req, "TPM2_PCR_Allocate") return active, ret
def read_file(filename): """ read contents from a file """ try: fobj = open(filename, mode='rb') result = fobj.read() fobj.close() return result, 0 except Exception as err: logerr(LOGFILE, "Could not read from file %s: %s\n" % \ (filename, str(err))) return "", 1
def read_file_lines(filename): """ Read the lines from a file and return a list of the lines """ try: fobj = open(filename, 'r') lines = fobj.readlines() fobj.close() return lines, 0 except Exception as err: logerr(LOGFILE, "Could not access %s to get name of certificate tool " "to invoke: %s\n" % (filename, str(err))) return [], 1
def delete_state(flags, tpm_state_path): """ Delete the TPM's state file """ if flags & SETUP_TPM2_F: statefile = "tpm2-00.permall" else: statefile = "tpm-00.permall" filepath = os.path.join(tpm_state_path, statefile) try: os.unlink(os.path.join(tpm_state_path, statefile)) except Exception as err: logerr(LOGFILE, "Could not remove state file %s: %s\n" % \ (filepath, str(err)))
def create_ek(self, isecc, rsa_keysize, allowsigning, decryption, lock_nvram): """ Create an ECC or RSA EK """ if isecc: tpm2_ek_handle = TPM2_EK_ECC_SECP384R1_HANDLE keytype = "ECC" nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKTEMPLATE else: if rsa_keysize == 2048: tpm2_ek_handle = TPM2_EK_RSA_HANDLE nvindex = TPM2_NV_INDEX_RSA2048_EKTEMPLATE elif rsa_keysize == 3072: tpm2_ek_handle = TPM2_EK_RSA3072_HANDLE nvindex = TPM2_NV_INDEX_RSA3072_HI_EKTEMPLATE keytype = "RSA %d" % rsa_keysize if isecc: ek_template, ekparam, handle, ret = \ self.createprimary_ek_ecc_nist_p384(allowsigning, decryption) else: ek_template, ekparam, handle, ret = \ self.createprimary_ek_rsa(rsa_keysize, allowsigning, decryption) if ret == 0: ret = self.evictcontrol(handle, tpm2_ek_handle) if ret != 0: logerr(self.logfile, "create_ek failed\n") return "", 1 logit( self.logfile, "Successfully created %s EK with handle 0x%x.\n" % (keytype, tpm2_ek_handle)) if allowsigning: nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, ek_template, lock_nvram, "EK template") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for %s EK template.\n" % (nvindex, keytype)) return ekparam, ret
def _createprimary_ecc(self, primaryhandle, keyflags, symkeydata, authpolicy, curveid, hashalg, nonce, off): """ Create an ECC key with the given parameters """ authblock = struct.pack('>IHBH', TPM2_RS_PW, 0, 0, 0) fmt = '>HHI H%ds %ds HH H %ds%ds' % \ (len(authpolicy), len(symkeydata), len(nonce), len(nonce)) public = struct.pack(fmt, TPM2_ALG_ECC, hashalg, keyflags, len(authpolicy), authpolicy, symkeydata, TPM2_ALG_NULL, curveid, TPM2_ALG_NULL, nonce, nonce) ek_template = public fmt = '>HII I I%ds HI H%ds IH' % (len(authblock), len(public)) req = struct.pack(fmt, TPM2_ST_SESSIONS, struct.calcsize(fmt), TPM2_CC_CREATEPRIMARY, primaryhandle, len(authblock), authblock, 4, 0, len(public), public, 0, 0) rsp, ret = self.transfer(req, "TPM2_CreatePrimary") if ret != 0: return b'', "", 0, 1 handle = struct.unpack('>I', rsp[10:14])[0] if curveid == TPM2_ECC_NIST_P384: exp_ksize = 48 cid = "secp384r1" else: logerr(self.logfile, "Unknown curveid 0x%x\n" % curveid) return b'', "", 0, 1 ksize1 = struct.unpack('>H', rsp[off:off + 2])[0] off2 = off + 2 + ksize1 ksize2 = struct.unpack('>H', rsp[off2:off2 + 2])[0] if ksize1 != exp_ksize or ksize2 != exp_ksize: logerr(self.logfile, "ECC: Getting key parameters from wrong offset\n") return b'', "", 0, 1 off += 2 xparam = struct.unpack(">%ds" % ksize1, rsp[off:off + ksize1])[0] off2 += 2 yparam = struct.unpack(">%ds" % ksize2, rsp[off2:off2 + ksize2])[0] ekparam = "x=%s,y=%s,id=%s" % (xparam.hex(), yparam.hex(), cid) return ek_template, ekparam, handle, 0
def check_state_overwrite(flags, tpm_state_path): """ Check whether we are allowed to overwrite existing state """ if flags & SETUP_TPM2_F: statefile = "tpm2-00.permall" else: statefile = "tpm-00.permall" if os.access(os.path.join(tpm_state_path, statefile), os.R_OK|os.W_OK): if flags & SETUP_STATE_NOT_OVERWRITE_F: logit(LOGFILE, "Not overwriting existing state file.\n") return 2 if flags & SETUP_STATE_OVERWRITE_F: return 0 logerr(LOGFILE, "Found existing TPM state file %s.\n" % statefile) return 1 return 0
def _createprimary_rsa(self, primaryhandle, keyflags, symkeydata, authpolicy, rsa_keysize, havenonce, off): """ Create an RSA key with the given parameters """ if rsa_keysize == 2048: nonce = NONCE_RSA2048 hashalg = TPM2_ALG_SHA256 elif rsa_keysize == 3072: if not havenonce: nonce = NONCE_EMPTY else: nonce = NONCE_RSA3072 hashalg = TPM2_ALG_SHA384 else: logerr(self.logfile, "Unsupported keysize %d\n" % rsa_keysize) return b'', "", 0, 1 authblock = struct.pack('>IHBH', TPM2_RS_PW, 0, 0, 0) fmt = '>HHI H%ds %ds HH I %ds' % \ (len(authpolicy), len(symkeydata), len(nonce)) public = struct.pack(fmt, TPM2_ALG_RSA, hashalg, keyflags, len(authpolicy), authpolicy, symkeydata, TPM2_ALG_NULL, rsa_keysize, 0, nonce) ek_template = public fmt = ">HII I I%ds HI H%ds IH" % (len(authblock), len(public)) req = struct.pack(fmt, TPM2_ST_SESSIONS, struct.calcsize(fmt), TPM2_CC_CREATEPRIMARY, primaryhandle, len(authblock), authblock, 4, 0, len(public), public, 0, 0) rsp, ret = self.transfer(req, "TPM2_CreatePrimary") if ret != 0: return b'', "", 0, 1 handle = struct.unpack(">I", rsp[10:14])[0] modlen = struct.unpack(">H", rsp[off:off + 2])[0] if modlen != rsa_keysize >> 3: logerr(self.logfile, "RSA key: Getting modulus from wrong offset %d\n" % off) return b'', "", 0, 1 off += 2 ekparam = struct.unpack(">%ds" % modlen, rsp[off:off + modlen])[0].hex() return ek_template, ekparam, handle, 0
def create_endorsement_key_pair(self): """ Create an endorsement key for the TPM 1.2 """ req = b'\x00\xc1\x00\x00\x00\x36\x00\x00\x00\x78\x38\xf0\x30\x81\x07\x2b' \ b'\x0c\xa9\x10\x98\x08\xc0\x4B\x05\x11\xc9\x50\x23\x52\xc4\x00\x00' \ b'\x00\x01\x00\x03\x00\x02\x00\x00\x00\x0c\x00\x00\x08\x00\x00\x00' \ b'\x00\x02\x00\x00\x00\x00' rsp, ret = self.transfer(req, "TPM_CreateEndorsementKeyPair") if ret != 0: return b'', 1 length = struct.unpack(">I", rsp[34:38])[0] if length != 256: logerr(self.logfile, "Offset to EK Public key is wrong.\n") return b'', 1 pubek = struct.unpack("256s", rsp[38:38 + 256])[0] return pubek, 0
def transfer(self, req, cmdname, use_ctrl=False): """ Send a command to swtpm and receive a response """ if use_ctrl: sock = self.ctrl_client_socket offset = 0 else: sock = self.data_client_socket offset = 6 try: sock.sendall(req) rsp = sock.recv(4096) except Exception as err: logerr(self.logfile, "transfer error: %s\n" % str(err)) return None, 1 if not use_ctrl: if len(rsp) < 10: logerr( self.logfile, "Response for %s has only %d bytes.\n" % (cmdname, len(rsp))) return None, 1 returncode = struct.unpack(">I", rsp[offset:offset + 4])[0] if returncode != 0: logerr(self.logfile, "%s failed: 0x%x\n" % (cmdname, returncode)) return None, 1 return rsp, 0
def get_rsa_keysizes(flags, swtpm_prg_l): """ Get the support RSA key sizes @return This function returns a list of ints like the following - [ 1024, 2048, 3072 ] - [] (empty list, indicating only 2048 bit RSA keys are supported) """ res = [] if flags & SETUP_TPM2_F: cmdarray = swtpm_prg_l.copy() cmdarray.extend(["--tpm2", "--print-capabilities"]) try: process = subprocess.Popen(cmdarray, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = process.communicate() try: instr = stdout.decode() j = json.loads(instr) for entry in j["features"]: if entry.startswith("rsa-keysize-"): try: res.append(int(entry[12:])) except ValueError as err: logerr(LOGFILE, "Internal error: Could not parse '%s' as int: %s\n" % (entry[12:], str(err))) return [], 1 except json.decoder.JSONDecodeError as err: logerr(LOGFILE, "Internal error: Could not parse '%s' as JSON: %s\n" % (instr, str(err))) return [], 1 except Exception as err: logerr(LOGFILE, "Could not start swtpm '%s': %s\n" % (" ".join(swtpm_prg_l), str(err))) return res, 1 return res, 0
def write_nvram(self, nvindex, nvindexattrs, data, lock_nvram, purpose): """ Define NVRAM space, write data to it and lock it if wanted """ ret = self.nv_definespace(nvindex, nvindexattrs, len(data)) if ret != 0: logerr( self.logfile, "Could not create NVRAM area 0x%x for %s.\n" % (nvindex, purpose)) return 1 ret = self.nv_write(nvindex, data) if ret != 0: logerr( self.logfile, "Could not write %s into NVRAM area 0x%x.\n" % (purpose, nvindex)) return 1 if lock_nvram: ret = self.nv_writelock(nvindex) if ret != 0: logerr( self.logfile, "Could not lock EK template NVRAM area 0x%x.\n" % nvindex) return 1 return ret
def init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, swtpm_keyopt, fds_to_pass): """ Initialize a TPM 1.2: create keys and certificate and take ownership """ certsdir = tpm_state_path swtpm = Swtpm12(swtpm_prg_l.copy(), tpm_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) ret = swtpm.start() if ret != 0: logerr(LOGFILE, "Could not start the TPM 2.\n") return 1 # We will have to call swtpm.destroy() at the end if ret == 0: ret = swtpm.run_swtpm_bios() if ret == 0 and flags & SETUP_CREATE_EK_F: pubek, ret = swtpm.create_endorsement_key_pair() if ret == 0: logit(LOGFILE, "Successfully created EK.\n") if ret == 0 and flags & SETUP_TAKEOWN_F: ret = tpm12_take_ownership(flags, ownerpass, srkpass, pubek, swtpm) if ret == 0: logit(LOGFILE, "Successfully took ownership of the TPM.\n") if ret == 0 and flags & SETUP_EK_CERT_F: ekparam = pubek.hex() ret = tpm12_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm) if ret == 0 and flags & SETUP_LOCK_NVRAM_F: ret = swtpm.nv_lock() if ret == 0: logit(LOGFILE, "Successfully locked NVRAM access.\n") swtpm.destroy() return ret
def init_tpm2(flags, swtpm_prg_l, config_file, tpm2_state_path, vmid, pcr_banks, swtpm_keyopt, fds_to_pass, rsa_keysize): """ Initialize a TPM 2.0: create keys and certificate """ certsdir = tpm2_state_path swtpm = Swtpm2(swtpm_prg_l.copy(), tpm2_state_path, swtpm_keyopt, LOGFILE, fds_to_pass) ret = swtpm.start() if ret != 0: logerr(LOGFILE, "Could not start the TPM 2.\n") return 1 if ret == 0: ret = swtpm.run_swtpm_bios() if ret == 0 and flags & SETUP_CREATE_SPK_F: ret = swtpm.create_spk(flags & SETUP_TPM2_ECC_F, rsa_keysize) if ret == 0: ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm) if ret == 0 and pcr_banks != "-": all_pcr_banks, ret = swtpm.get_all_pcr_banks() if ret == 0: active_pcr_banks, ret = swtpm.set_active_pcr_banks( pcr_banks.split(","), all_pcr_banks) if ret == 0: logit( LOGFILE, "Successfully activated PCR banks %s among %s.\n" % (",".join(active_pcr_banks), ",".join(all_pcr_banks))) if ret == 0: swtpm.shutdown() swtpm.destroy() return ret
def createprimary_ek_ecc_nist_p384(self, allowsigning, decryption): """ Create en ECC EK key that may be allowed to sign and/or decrypt """ if allowsigning and decryption: # keyflags: fixedTPM, fixedParent, sensitiveDatOrigin, # userWithAuth, adminWithPolicy, sign, decrypt keyflags = 0x000600f2 # symmetric: TPM_ALG_NULL symkeydata = struct.pack(">H", TPM2_ALG_NULL) off = 86 elif allowsigning: # keyflags: fixedTPM, fixedParent, sensitiveDatOrigin, # userWithAuth, adminWithPolicy, sign keyflags = 0x000400f2 # symmetric: TPM_ALG_NULL symkeydata = struct.pack(">H", TPM2_ALG_NULL) off = 86 else: # keyflags: fixedTPM, fixedParent, sensitiveDatOrigin, # userWithAuth, adminWithPolicy, restricted, decrypt keyflags = 0x000300f2 # symmetric: TPM_ALG_AES, 256bit, TPM_ALG_CFB symkeydata = struct.pack(">HHH", TPM2_ALG_AES, 256, TPM2_ALG_CFB) off = 90 # authPolicy from Ek Credential Profile; Spec v 2.1; rev12; p. 43 authpolicy = b'\xB2\x6E\x7D\x28\xD1\x1A\x50\xBC\x53\xD8\x82\xBC' \ b'\xF5\xFD\x3A\x1A\x07\x41\x48\xBB\x35\xD3\xB4\xE4' \ b'\xCB\x1C\x0A\xD9\xBD\xE4\x19\xCA\xCB\x47\xBA\x09' \ b'\x69\x96\x46\x15\x0F\x9F\xC0\x00\xF3\xF8\x0E\x12' ek_template, ekparam, handle, ret = \ self._createprimary_ecc(TPM2_RH_ENDORSEMENT, keyflags, symkeydata, authpolicy, TPM2_ECC_NIST_P384, TPM2_ALG_SHA384, NONCE_EMPTY, off) if ret != 0: logerr(self.logfile, "create_spk_ecc failed\n") return ek_template, ekparam, handle, ret
def write_platform_cert_nvram(self, lock_nvram, platformcert): """ Write the platform certificate into an NVRAM area """ nvindex = TPM2_NV_INDEX_PLATFORMCERT nvindexattrs = TPMA_NV_PLATFORMCREATE | \ TPMA_NV_AUTHREAD | \ TPMA_NV_OWNERREAD | \ TPMA_NV_PPREAD | \ TPMA_NV_PPWRITE | \ TPMA_NV_NO_DA | \ TPMA_NV_WRITEDEFINE ret = self.write_nvram(nvindex, nvindexattrs, platformcert, lock_nvram, "Platform Certificate") if ret == 0: logit( self.logfile, "Successfully created NVRAM area 0x%x for platform certificate.\n" % nvindex) else: logerr( self.logfile, "Could not create NVRAM area 0x%x for platform certificate.\n" % nvindex) return ret
def call_create_certs(flags, config_file, certsdir, ekparam, vmid, swtpm): """ Call an external tool to create the certificates """ params, ret = tpm_get_specs_and_attributes(swtpm) if ret != 0: return 1 lines, ret = read_file_lines(config_file) if ret != 0: return ret create_certs_tool = get_config_value(lines, "create_certs_tool") create_certs_tool_config = get_config_value(lines, "create_certs_tool_config") create_certs_tool_options = get_config_value(lines, "create_certs_tool_options") ret = 0 if create_certs_tool: ret = 1 if flags & SETUP_TPM2_F: params.extend(["--tpm2"]) cmd = [create_certs_tool, "--type", "_", "--ek", ekparam, "--dir", certsdir] if len(LOGFILE) > 0: cmd.extend(["--logfile", LOGFILE]) if len(vmid) > 0: cmd.extend(["--vmid", vmid]) cmd.extend(params) if create_certs_tool_config: cmd.extend(["--configfile", create_certs_tool_config]) if create_certs_tool_options: cmd.extend(["--optsfile", create_certs_tool_options]) try: i = create_certs_tool.rindex(os.sep) prgname = create_certs_tool[i + 1:] except ValueError: prgname = create_certs_tool for entry in [(SETUP_EK_CERT_F, "ek"), (SETUP_PLATFORM_CERT_F, "platform")]: if flags & entry[0]: try: cmd[2] = entry[1] logit(LOGFILE, " Invoking %s\n" % (" ".join(cmd))) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = proc.communicate(timeout=30) for line in stdout.decode().split("\n"): if len(line) > 0: logit(LOGFILE, "%s: %s\n" % (prgname, line)) ret = proc.returncode if ret != 0: logerr(LOGFILE, "Error returned from command\n") return 1 except FileNotFoundError as err: logerr(LOGFILE, "Could not execute %s: %s\n" % (create_certs_tool, str(err))) return 1 except subprocess.TimeoutExpired as err: logerr(LOGFILE, "%s did not finish in time: %s\n" % (create_certs_tool, str(err))) return 1 except Exception as err: logerr(LOGFILE, "An error occurred running %s: %s\n" % (create_certs_tool, str(err))) return ret
def main(): """ main function - parses command line parameters and low level dealing with them """ global LOGFILE # pylint: disable=W0603 swtpm_prg = distutils.spawn.find_executable("swtpm") if swtpm_prg: swtpm_prg += " socket" try: opts, _ = getopt.getopt(sys.argv[1:], "h?", ["tpm-state=", "tpmstate=", "tpm=", "swtpm_ioctl=", "tpm2", "ecc", "createek", "create-spk", "take-ownership", "ownerpass="******"owner-well-known", "srkpass="******"srk-well-known", "create-ek-cert", "create-platform-cert", "lock-nvram", "display", "config=", "vmid=", "keyfile=", "keyfile-fd=", "pwdfile=", "pwdfile-fd=", "cipher=", "runas=", "logfile=", "overwrite", "not-overwrite", "allow-signing", "decryption", "pcr-banks=", "rsa-keysize=", "tcsd-system-ps-file", "version", "print-capabilities", "help"]) except getopt.GetoptError as err: print(err) usage(sys.argv[0]) sys.exit(1) flags = 0 tpm_state_path = "" config_file = DEFAULT_CONFIG_FILE ownerpass = None got_ownerpass = False srkpass = None got_srkpass = False vmid = "" pcr_banks = "" printcapabilities = False keyfile = "" keyfile_fd = "" pwdfile = "" pwdfile_fd = "" cipher = "aes-128-cbc" rsa_keysize_str = "%d" % DEFAULT_RSA_KEYSIZE swtpm_keyopt = "" fds_to_pass = [] runas = "" for opt, arg in opts: if opt in ['--tpm-state', '--tpmstate']: tpm_state_path = arg elif opt == '--tpm': swtpm_prg = arg elif opt == '--swtpm_ioctl': print("Warning: --swtpm_ioctl is deprecated and has no effect.") elif opt == '--tpm2': flags |= SETUP_TPM2_F elif opt == '--ecc': flags |= SETUP_TPM2_ECC_F elif opt == '--createek': flags |= SETUP_CREATE_EK_F elif opt == '--create-spk': flags |= SETUP_CREATE_SPK_F elif opt == '--take-ownership': flags |= SETUP_CREATE_EK_F | SETUP_TAKEOWN_F elif opt == '--ownerpass': ownerpass = arg got_ownerpass = True elif opt == '--owner-well-known': flags |= SETUP_OWNERPASS_ZEROS_F elif opt == '--srkpass': srkpass = arg got_srkpass = True elif opt == '--srk-well-known': flags |= SETUP_SRKPASS_ZEROS_F elif opt == '--create-ek-cert': flags |= SETUP_CREATE_EK_F | SETUP_EK_CERT_F elif opt == '--create-platform-cert': flags |= SETUP_CREATE_EK_F | SETUP_PLATFORM_CERT_F elif opt == '--lock-nvram': flags |= SETUP_LOCK_NVRAM_F elif opt == '--display': flags |= SETUP_DISPLAY_RESULTS_F elif opt == '--config': config_file = arg elif opt == '--vmid': vmid = arg elif opt == '--keyfile': keyfile = arg elif opt == '--keyfile-fd': keyfile_fd = arg elif opt == '--pwdfile': pwdfile = arg elif opt == '--pwdfile-fd': pwdfile_fd = arg elif opt == '--cipher': cipher = arg elif opt == '--runas': runas = arg elif opt == '--logfile': LOGFILE = arg elif opt == '--overwrite': flags |= SETUP_STATE_OVERWRITE_F elif opt == '--not-overwrite': flags |= SETUP_STATE_NOT_OVERWRITE_F elif opt == '--allow-signing': flags |= SETUP_ALLOW_SIGNING_F elif opt == '--decryption': flags |= SETUP_DECRYPTION_F elif opt == '--pcr-banks': pcr_banks = pcr_banks + "," + arg elif opt == '--rsa-keysize': rsa_keysize_str = arg elif opt == '--tcsd-system-ps-file': print("Warning: --tcsd-system-ps-file is deprecated and has no effect.") elif opt == '--version': versioninfo() sys.exit(0) elif opt == '--print-capabilities': printcapabilities = True elif opt in ['--help', '-h', '-?']: usage(sys.argv[0]) sys.exit(0) else: sys.stderr.write("Unknown option %s\n" % opt) usage(sys.argv[0]) sys.exit(1) if not swtpm_prg: logerr(LOGFILE, "Default TPM 'swtpm' could not be found and was not provided using --tpm\n.") sys.exit(1) swtpm_prg_l = swtpm_prg.split() if not distutils.spawn.find_executable(swtpm_prg_l[0]): logerr(LOGFILE, "TPM at %s is not an executable.\n" % (" ".join(swtpm_prg_l))) sys.exit(1) if printcapabilities: ret = print_capabilities(swtpm_prg_l) sys.exit(ret) if runas: ret = change_process_owner(runas) if ret != 0: sys.exit(1) if not got_ownerpass: flags |= SETUP_OWNERPASS_ZEROS_F if not got_srkpass: flags |= SETUP_SRKPASS_ZEROS_F # sequeeze ',' and remove leading and trailing ',' pcr_banks = re.sub(r',$', '', re.sub(r'^,', '', re.sub(r',,+', ',', pcr_banks))) if len(pcr_banks) == 0: pcr_banks = DEFAULT_PCR_BANKS # set owner password to default if user didn't provide any password wish # and wants to take ownership if flags & SETUP_TAKEOWN_F and \ flags & SETUP_OWNERPASS_ZEROS_F and \ not got_srkpass: srkpass = DEFAULT_SRK_PASSWORD if len(LOGFILE) > 0: if os.path.islink(LOGFILE): sys.stderr.write("Logfile must not be a symlink.\n") sys.exit(1) try: fobj = open(LOGFILE, "a") # do not truncate fobj.close() except PermissionError: sys.stderr.write("Cannot write to logfile %s.\n", LOGFILE) sys.exit(1) # Check tpm_state_path directory and access rights if len(tpm_state_path) == 0: logerr(LOGFILE, "--tpm-state must be provided\n") sys.exit(1) if not os.path.isdir(tpm_state_path): logerr(LOGFILE, "User %s cannot access directory %s. Make sure it exists and is a directory.\n" % (getpass.getuser(), tpm_state_path)) sys.exit(1) if not os.access(tpm_state_path, os.R_OK): logerr(LOGFILE, "Need read rights on directory %s for user %s.\n" % (tpm_state_path, getpass.getuser())) sys.exit(1) if not os.access(tpm_state_path, os.W_OK): logerr(LOGFILE, "Need write rights on directory %s for user %s.\n" % (tpm_state_path, getpass.getuser())) sys.exit(1) if flags & SETUP_TPM2_F: if flags & SETUP_TAKEOWN_F: logerr(LOGFILE, "Taking ownership is not supported for TPM 2.\n") sys.exit(1) else: if flags & SETUP_TPM2_ECC_F: logerr(LOGFILE, "--ecc requires --tpm2.\n") sys.exit(1) if flags & SETUP_CREATE_SPK_F: logerr(LOGFILE, "--create-spk requires --tpm2.\n") sys.exit(1) ret = check_state_overwrite(flags, tpm_state_path) if ret == 1: sys.exit(1) elif ret == 2: sys.exit(0) files = glob.glob(os.path.join(tpm_state_path, "*permall")) files.extend(glob.glob(os.path.join(tpm_state_path, "*volatilestate"))) files.extend(glob.glob(os.path.join(tpm_state_path, "*savestate"))) try: for fil in files: os.remove(fil) except Exception as err: logerr(LOGFILE, "Could not remove previous state files. Need execute access rights on the " "directory %s.\n" % tpm_state_path) sys.exit(1) lockfile = os.path.join(tpm_state_path, ".lock") if os.path.exists(lockfile) and not os.access(lockfile, os.W_OK | os.R_OK): logerr(LOGFILE, "User %s cannot read/write %s.\n" % (getpass.getuser(), lockfile)) sys.exit(1) if not os.access(config_file, os.R_OK): logerr(LOGFILE, "User %s cannot read %s.\n" % (getpass.getuser(), config_file)) sys.exit(1) if len(cipher) > 0: if cipher not in ['aes-128-cbc', 'aes-cbc', 'aes-256-cbc']: logerr(LOGFILE, "Unsupported cipher %s.\n" % cipher) sys.exit(1) cipher = ",mode=%s" % cipher if len(keyfile) > 0: if not os.access(keyfile, os.R_OK): logerr(LOGFILE, "User %s cannot read keyfile %s.\n" % (getpass.getuser(), keyfile)) sys.exit(1) swtpm_keyopt = "file=%s%s" % (keyfile, cipher) logit(LOGFILE, " The TPM's state will be encrypted with a provided key.\n") elif len(pwdfile) > 0: if not os.access(pwdfile, os.R_OK): logerr(LOGFILE, "User %s canot read passphrase file %s.\n" % \ (getpass.getuser(), keyfile)) sys.exit(1) swtpm_keyopt = "pwdfile=%s%s" % (pwdfile, cipher) logit(LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase.\n") elif len(keyfile_fd) > 0: if not keyfile_fd.isnumeric(): logerr(LOGFILE, "--keyfile-fd parameter $keyfile_fd is not a valid file descriptor.\n") sys.exit(1) fds_to_pass.append(int(keyfile_fd)) swtpm_keyopt = "fd=%s%s" % (keyfile_fd, cipher) logit(LOGFILE, " The TPM's state will be encrypted with a provided key (fd).\n") elif len(pwdfile_fd) > 0: if not pwdfile_fd.isnumeric(): logerr(LOGFILE, "--pwdfile-fd parameter $pwdfile_fd is not a valid file descriptor.\n") sys.exit(1) fds_to_pass.append(int(pwdfile_fd)) swtpm_keyopt = "pwdfd=%s%s" % (pwdfile_fd, cipher) logit(LOGFILE, " The TPM's state will be encrypted using a key derived from a passphrase (fd).\n") if rsa_keysize_str == "max": keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) if ret != 0: sys.exit(1) if len(keysizes) > 0: rsa_keysize_str = keysizes[-1] else: rsa_keysize_str = "2048" if rsa_keysize_str in ["2048", "3072"]: rsa_keysize = int(rsa_keysize_str) supported_keysizes, ret = get_rsa_keysizes(flags, swtpm_prg_l) if ret != 0: sys.exit(1) if rsa_keysize not in supported_keysizes and rsa_keysize != 2048: logerr(LOGFILE, "%s bit RSA keys are not supported by libtpms.\n" % rsa_keysize_str) sys.exit(1) else: logerr(LOGFILE, "Unsupported RSA key size %s.\n" % rsa_keysize_str) sys.exit(1) user = getpass.getuser() try: group = grp.getgrnam(user)[0] except KeyError: group = "<unknown>" tzinfo = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo logit(LOGFILE, "Starting vTPM manufacturing as %s:%s @ %s\n" % (user, group, datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z"))) if not flags & SETUP_TPM2_F: ret = init_tpm(flags, swtpm_prg_l, config_file, tpm_state_path, ownerpass, srkpass, vmid, swtpm_keyopt, fds_to_pass) else: ret = init_tpm2(flags, swtpm_prg_l, config_file, tpm_state_path, vmid, pcr_banks, swtpm_keyopt, fds_to_pass, rsa_keysize) if ret == 0: logit(LOGFILE, "Successfully authored TPM state.\n") else: logerr(LOGFILE, "An error occurred. Authoring the TPM state failed.\n") delete_state(flags, tpm_state_path) logit(LOGFILE, "Ending vTPM manufacturing @ %s\n" % datetime.datetime.now(tz=tzinfo).strftime("%a %d %h %Y %I:%M:%S %p %Z")) sys.exit(ret)
def start(self): """ The start method starts the TPM 2 """ self.pidfile = os.path.join(self.state_path, ".swtpm_setup.pidfile") cmdline = self.swtpm_exec_l.copy() if self.is_tpm2: cmdline.extend(["--tpm2"]) if self.keyopt: cmdline.extend(["--key", self.keyopt]) cmdline.extend([ "--flags", "not-need-init", "--tpmstate", "dir=%s" % self.state_path, "--pid", "file=%s" % self.pidfile ]) # cmdline.extend(["--log", "file=/tmp/log,level=20"]) ctr = 0 while ctr < 100: self.data_client_socket, self.data_swtpm_socket = socket.socketpair( socket.AF_UNIX, socket.SOCK_STREAM) os.set_inheritable(self.data_swtpm_socket.fileno(), True) self.ctrl_client_socket, self.ctrl_swtpm_socket = socket.socketpair( socket.AF_UNIX, socket.SOCK_STREAM) os.set_inheritable(self.ctrl_swtpm_socket.fileno(), True) r_cmdline = cmdline.copy() r_cmdline.extend([ "--server", "type=tcp,fd=%d" % self.data_swtpm_socket.fileno(), "--ctrl", "type=unixio,clientfd=%d" % self.ctrl_swtpm_socket.fileno() ]) self.remove_pidfile() # print("starting swtpm: %s\n" % r_cmdline) try: pass_fds = [ self.data_swtpm_socket.fileno(), self.ctrl_swtpm_socket.fileno() ] pass_fds.extend(self.fds_to_pass) self.swtpm_proc = subprocess.Popen(r_cmdline, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, pass_fds=pass_fds) except Exception as err: logerr( self.logfile, "Failed to start swtpm %s: %s\n" % (" ".join(self.swtpm_exec_l), str(err))) ctr += 1 ctr2 = 0 while True: # Is it still running? if self.swtpm_proc.poll(): stderr = self.swtpm_proc.communicate()[0] print("TPM died? %s\n" % stderr) self.stop() break if os.path.exists(self.pidfile): print("TPM is listening on Unix socket.") return 0 ctr2 += 1 time.sleep(0.05) if ctr2 == 40: self.stop() break return 1