def makedir(dirname, purpose): """ Create a directory if it does not exist """ if not os.path.exists(dirname): logit(LOGFILE, "Creating swtpm-local dir '%s'.\n" % dirname) try: os.makedirs(dirname) except OSError as err: logerr(LOGFILE, "Could not create directory for '%s': %s\n" % (purpose, str(err))) return 1 return 0
def main(): """ main function - parses command line parameters and low level dealing with them """ global LOGFILE # pylint: disable=W0603 try: opts, _ = getopt.getopt(sys.argv[1:], "h?", [ "type=", "ek=", "dir=", "vmid=", "optsfile=", "configfile=", "logfile=", "tpm-spec-family=", "tpm-spec-revision=", "tpm-spec-level=", "tpm-manufacturer=", "tpm-model=", "tpm-version=", "tpm2", "allow-signing", "decryption", "help" ]) except getopt.GetoptError as err: print(err) usage(sys.argv[0]) sys.exit(1) flags = 0 typ = "" ekparams = "" directory = "" vmid = "" optsfile = DEFAULT_LOCALCA_OPTIONS configfile = DEFAULT_LOCALCA_CONFIG tpm_spec_params = [] tpm_attr_params = [] for opt, arg in opts: if opt == '--type': typ = arg elif opt == '--ek': ekparams = arg elif opt == '--dir': directory = arg elif opt == '--vmid': vmid = arg elif opt == '--optsfile': optsfile = arg elif opt == '--configfile': configfile = arg elif opt == '--logfile': LOGFILE = arg elif opt in [ '--tpm-spec-family', '--tpm-spec-revision', '--tpm-spec-level' ]: tpm_spec_params.extend([opt, arg]) elif opt in ['--tpm-manufacturer', '--tpm-model', '--tpm-version']: tpm_attr_params.extend([opt, arg]) elif opt == '--tpm2': flags |= SETUP_TPM2_F elif opt == '--allow-signing': flags |= ALLOW_SIGNING_F elif opt == '--decryption': flags |= DECRYPTION_F elif opt in ['--help', '-h', '-?']: usage(sys.argv[0]) sys.exit(0) if len(LOGFILE) > 0: try: fobj = open(LOGFILE, "w") fobj.close() except PermissionError: sys.stderr.write("Cannot write to logfile %s.\n", LOGFILE) sys.exit(1) if not os.access(optsfile, os.R_OK): logerr( LOGFILE, "Need read rights on options file %s for user %s.\n" % (optsfile, getpass.getuser())) sys.exit(1) if not os.access(configfile, os.R_OK): logerr( LOGFILE, "Need read rights on options file %s for user %s.\n" % (configfile, getpass.getuser())) sys.exit(1) lines, ret = read_file_lines(configfile) if ret != 0: sys.exit(1) statedir = get_config_value(lines, "statedir") if not statedir: logerr( LOGFILE, "Missing 'statedir' config value in config file %s.\n" % configfile) sys.exit(1) if not os.access(statedir, os.W_OK | os.R_OK): logerr( LOGFILE, "Need read/write rights on statedir %s for user %s.\n" % (statedir, getpass.getuser())) if makedir(statedir, "statedir") != 0: sys.exit(1) lockfile = os.path.join(statedir, ".lock.swtpm-localca") if os.path.exists(lockfile) and not os.access(lockfile, os.W_OK | os.R_OK): logerr( LOGFILE, "Need read/write rights on %s for user %s.\n" % (lockfile, getpass.getuser())) sys.exit(1) signkey = get_config_value(lines, "signingkey") if not signkey: logerr( LOGFILE, "Missing 'signingkey' config value in config file %s.\n" % configfile) sys.exit(1) # SIGNKEY may be a GNUTLS url like tpmkey:file= or tpmkey:uuid= if not signkey.startswith("tpmkey:file=") and \ not signkey.startswith("tpmkey:uuid=") and \ not signkey.startswith("pkcs11:"): if makedir(os.path.dirname(signkey), "signkey") != 0: sys.exit(1) signkey_password = get_config_value(lines, "signingkey_password") parentkey_password = get_config_value(lines, "parentkey_password") issuercert = get_config_value(lines, 'issuercert') if not issuercert: logerr( LOGFILE, "Missing 'issuercert' config value in config file %s.\n" % configfile) sys.exit(1) if makedir(os.path.dirname(issuercert), "issuercert") != 0: sys.exit(1) # environment needed for calling swtpm_cert swtpm_cert_env = os.environ # TPM keys are GNUTLS URIs... if signkey.startswith("tpmkey:file=") or signkey.startswith( "tpmkey:uuid="): tss_tcsd_hostname = get_config_value(lines, "TSS_TCSD_HOSTNAME", "localhost") tss_tcsd_port = get_config_value(lines, "TSS_TCSD_PORT", 30003) swtpm_cert_env["TSS_TCSD_HOSTNAME"] = tss_tcsd_hostname swtpm_cert_env["TSS_TCSD_PORT"] = tss_tcsd_port logit(LOGFILE, "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=%s " \ "TSS_TCSD_PORT=%s\n" % (tss_tcsd_hostname, tss_tcsd_port)) elif signkey.startswith("pkcs11:"): signkey = signkey.replace(r"\;", ";") if signkey_password: swtpm_cert_env["SWTPM_PKCS11_PIN"] = signkey_password logit( LOGFILE, "CA uses a PKCS#11 key; using password from 'signingkey_password'\n" ) else: swtpm_pkcs11_pin = get_config_value(lines, "SWTPM_PKCS11_PIN", "swtpm-tpmca") swtpm_cert_env["SWTPM_PKCS11_PIN"] = swtpm_pkcs11_pin logit(LOGFILE, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n") # Get additional environment variables pkcs11 modules may need envvars, ret = get_config_envvars(lines) if ret != 0: sys.exit(1) swtpm_cert_env.update(envvars) else: # if signkey does not exists it will be created... if not os.access(signkey, os.R_OK): if os.path.exists(signkey): logerr( LOGFILE, "Need read rights on signing key %s for user %s.\n" % (signkey, getpass.getuser())) sys.exit(1) logit( LOGFILE, "Creating root CA and a local CA's signing key and issuer cert.\n" ) if create_localca_cert(lockfile, statedir, signkey, signkey_password, issuercert) != 0: logerr(LOGFILE, "Error creating local CA's signing key and cert.\n") sys.exit(1) if not os.access(signkey, os.R_OK): logerr( LOGFILE, "Need read rights on signing key %s for user %s.\n" % (signkey, getpass.getuser())) sys.exit(1) if not os.access(issuercert, os.R_OK): logerr( LOGFILE, "Need read rights on issuer certificate %s for user %s.\n" % (issuercert, getpass.getuser())) sys.exit(1) certserial = get_config_value(lines, "certserial", os.path.join(statedir, "certserial")) if makedir(os.path.dirname(certserial), "certserial") != 0: sys.exit(1) ret = create_cert(flags, typ, directory, ekparams, vmid, tpm_spec_params, tpm_attr_params, signkey, signkey_password, issuercert, parentkey_password, swtpm_cert_env, certserial, lockfile, optsfile) sys.exit(ret)
def create_cert(flags, typ, directory, ekparams, vmid, tpm_spec_params, tpm_attr_params, signkey, signkey_password, issuercert, parentkey_password, swtpm_cert_env, certserial, lockfile, optsfile): """ Create the certificate """ serial, ret = get_next_cert_serial(certserial, lockfile) if ret != 0: return 1 options = [] lines, _ = read_file_lines(optsfile) for line in lines: if not line.strip(): continue options.extend([x.strip() for x in line.split(" ", 1)]) if vmid: subj = "CN=%s" % vmid else: subj = "CN=unknown" if flags & SETUP_TPM2_F: options.append("--tpm2") else: options.append("--add-header") if typ == "ek": if flags & ALLOW_SIGNING_F: options.append("--allow-signing") if flags & DECRYPTION_F: options.append("--decryption") match = re.search(r'x=([0-9A-Fa-f]+),y=([0-9A-Fa-f]+)(,id=([^,]+))?', ekparams) if match: keyparams = ["--ecc-x", match.group(1), "--ecc-y", match.group(2)] if match.group(4): keyparams.extend(["--ecc-curveid", match.group(4)]) else: keyparams = ["--modulus", ekparams] cmd = ["swtpm_cert", "--subject", subj] cmd.extend(options) temp1 = None temp2 = None if signkey_password: temp1 = tempfile.NamedTemporaryFile() temp1.write(signkey_password.encode()) temp1.seek(0) cmd.extend(["--signkey-pwd", "file:%s" % temp1.name]) if parentkey_password: temp2 = tempfile.NamedTemporaryFile() temp2.write(parentkey_password.encode()) temp2.seek(0) cmd.extend(["--parentkey-pwd", "file:%s" % temp2.name]) if typ == "ek": cmd.extend(tpm_spec_params) cmd.extend(tpm_attr_params) if typ == "platform": cmd.extend([ "--type", "platform", "--out-cert", os.path.join(directory, "platform.cert") ]) else: cmd.extend(["--out-cert", os.path.join(directory, "ek.cert")]) cmd.extend(keyparams) cmd.extend([ "--signkey", signkey, "--issuercert", issuercert, "--days", "3650", "--serial", serial ]) if typ == "ek": certtype = "EK" else: certtype = "platform" try: proc = subprocess.Popen(cmd, env=swtpm_cert_env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = proc.communicate()[0] if proc.returncode: logerr(LOGFILE, "Could not create %s certificate locally\n" % certtype) logerr(LOGFILE, "%s" % output.decode()) return 1 except Exception as err: logerr(LOGFILE, "Could not run swtpm_cert: %s\n" % str(err)) return 1 finally: if temp1: temp1.close() if temp2: temp2.close() logit(LOGFILE, "Successfully created %s certificate locally.\n" % certtype) return 0