diff options
Diffstat (limited to 'security/keys/trusted-keys/trusted_tpm2.c')
-rw-r--r-- | security/keys/trusted-keys/trusted_tpm2.c | 161 |
1 files changed, 108 insertions, 53 deletions
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index bc700f85f80b..8b7dd73d94c1 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -38,6 +38,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, u8 *end_work = scratch + SCRATCH_SIZE; u8 *priv, *pub; u16 priv_len, pub_len; + int ret; priv_len = get_unaligned_be16(src) + 2; priv = src; @@ -57,8 +58,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, unsigned char bool[3], *w = bool; /* tag 0 is emptyAuth */ w = asn1_encode_boolean(w, w + sizeof(bool), true); - if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) - return PTR_ERR(w); + if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) { + ret = PTR_ERR(w); + goto err; + } work = asn1_encode_tag(work, end_work, 0, bool, w - bool); } @@ -69,8 +72,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, * trigger, so if it does there's something nefarious going on */ if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, - "BUG: scratch buffer is too small")) - return -EINVAL; + "BUG: scratch buffer is too small")) { + ret = -EINVAL; + goto err; + } work = asn1_encode_integer(work, end_work, options->keyhandle); work = asn1_encode_octet_string(work, end_work, pub, pub_len); @@ -79,10 +84,18 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, work1 = payload->blob; work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob), scratch, work - scratch); - if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed")) - return PTR_ERR(work1); + if (IS_ERR(work1)) { + ret = PTR_ERR(work1); + pr_err("BUG: ASN.1 encoder failed with %d\n", ret); + goto err; + } + kfree(scratch); return work1 - payload->blob; + +err: + kfree(scratch); + return ret; } struct tpm2_key_context { @@ -228,8 +241,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options) { + off_t offset = TPM_HEADER_SIZE; + struct tpm_buf buf, sized; int blob_len = 0; - struct tpm_buf buf; u32 hash; u32 flags; int i; @@ -252,50 +266,58 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (rc) return rc; + rc = tpm2_start_auth_session(chip); + if (rc) + goto out_put; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) { - tpm_put_ops(chip); - return rc; + tpm2_end_auth_session(chip); + goto out_put; } - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); + rc = tpm_buf_init_sized(&sized); + if (rc) { + tpm_buf_destroy(&buf); + tpm2_end_auth_session(chip); + goto out_put; + } + + tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, + options->keyauth, TPM_DIGEST_SIZE); /* sensitive */ - tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len); + tpm_buf_append_u16(&sized, options->blobauth_len); - tpm_buf_append_u16(&buf, options->blobauth_len); if (options->blobauth_len) - tpm_buf_append(&buf, options->blobauth, options->blobauth_len); + tpm_buf_append(&sized, options->blobauth, options->blobauth_len); - tpm_buf_append_u16(&buf, payload->key_len); - tpm_buf_append(&buf, payload->key, payload->key_len); + tpm_buf_append_u16(&sized, payload->key_len); + tpm_buf_append(&sized, payload->key, payload->key_len); + tpm_buf_append(&buf, sized.data, sized.length); /* public */ - tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, hash); + tpm_buf_reset_sized(&sized); + tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH); + tpm_buf_append_u16(&sized, hash); /* key properties */ flags = 0; flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; - flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | - TPM2_OA_FIXED_PARENT); - tpm_buf_append_u32(&buf, flags); + flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT); + tpm_buf_append_u32(&sized, flags); /* policy */ - tpm_buf_append_u16(&buf, options->policydigest_len); + tpm_buf_append_u16(&sized, options->policydigest_len); if (options->policydigest_len) - tpm_buf_append(&buf, options->policydigest, - options->policydigest_len); + tpm_buf_append(&sized, options->policydigest, options->policydigest_len); /* public parameters */ - tpm_buf_append_u16(&buf, TPM_ALG_NULL); - tpm_buf_append_u16(&buf, 0); + tpm_buf_append_u16(&sized, TPM_ALG_NULL); + tpm_buf_append_u16(&sized, 0); + + tpm_buf_append(&buf, sized.data, sized.length); /* outside info */ tpm_buf_append_u16(&buf, 0); @@ -305,28 +327,30 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; + tpm2_end_auth_session(chip); goto out; } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (rc) goto out; - blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); - if (blob_len > MAX_BLOB_SIZE) { + blob_len = tpm_buf_read_u32(&buf, &offset); + if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) { rc = -E2BIG; goto out; } - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { + if (buf.length - offset < blob_len) { rc = -EFAULT; goto out; } - blob_len = tpm2_key_encode(payload, options, - &buf.data[TPM_HEADER_SIZE + 4], - blob_len); + blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len); out: + tpm_buf_destroy(&sized); tpm_buf_destroy(&buf); if (rc > 0) { @@ -340,6 +364,7 @@ out: else payload->blob_len = blob_len; +out_put: tpm_put_ops(chip); return rc; } @@ -409,25 +434,31 @@ static int tpm2_load_cmd(struct tpm_chip *chip, if (blob_len > payload->blob_len) return -E2BIG; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + rc = tpm2_start_auth_session(chip); if (rc) return rc; - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (rc) { + tpm2_end_auth_session(chip); + return rc; + } + + tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth, + TPM_DIGEST_SIZE); tpm_buf_append(&buf, blob, blob_len); if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; + tpm2_end_auth_session(chip); goto out; } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -465,20 +496,44 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, u8 *data; int rc; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + rc = tpm2_start_auth_session(chip); if (rc) return rc; - tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, - options->policyhandle ? - options->policyhandle : TPM2_RS_PW, - NULL /* nonce */, 0, - TPM2_SA_CONTINUE_SESSION, - options->blobauth /* hmac */, - options->blobauth_len); + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + if (rc) { + tpm2_end_auth_session(chip); + return rc; + } + + tpm_buf_append_name(chip, &buf, blob_handle, NULL); + + if (!options->policyhandle) { + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, + options->blobauth, + options->blobauth_len); + } else { + /* + * FIXME: The policy session was generated outside the + * kernel so we don't known the nonce and thus can't + * calculate a HMAC on it. Therefore, the user can + * only really use TPM2_PolicyPassword and we must + * send down the plain text password, which could be + * intercepted. We can still encrypt the returned + * key, but that's small comfort since the interposer + * could repeat our actions with the exfiltrated + * password. + */ + tpm2_buf_append_auth(&buf, options->policyhandle, + NULL /* nonce */, 0, 0, + options->blobauth, options->blobauth_len); + tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT, + NULL, 0); + } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (rc > 0) rc = -EPERM; |