From patchwork Tue Dec 10 00:05:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281071 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8521B14B7 for ; Tue, 10 Dec 2019 00:05:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 63BAC2073D for ; Tue, 10 Dec 2019 00:05:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="qKTSnpFV"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="qKTSnpFV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727109AbfLJAFb (ORCPT ); Mon, 9 Dec 2019 19:05:31 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:37854 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAFa (ORCPT ); Mon, 9 Dec 2019 19:05:30 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id AAF048EE112; Mon, 9 Dec 2019 16:05:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936330; bh=d1fuSknOHfbVoIjckfTDmfT5wxztl0cmhyOV1XejJTE=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=qKTSnpFVsPeiyEobwnvlvOOuHJjyum034LhQNz9AqzaPD+hiWq9H0Hv29zjat5xlk Zx1uKrHWNSRyXUH2Tki4Pzoh7rDDAc3CSlve7fch9uuRS9L08wbcbx1g1SeuG2i7Oh HqJR4jTOWTiK9eXFHrKH5c2U++logXjTnNa+IKh0= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1VEohNMelbxY; Mon, 9 Dec 2019 16:05:30 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 374A58EE0FC; Mon, 9 Dec 2019 16:05:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936330; bh=d1fuSknOHfbVoIjckfTDmfT5wxztl0cmhyOV1XejJTE=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=qKTSnpFVsPeiyEobwnvlvOOuHJjyum034LhQNz9AqzaPD+hiWq9H0Hv29zjat5xlk Zx1uKrHWNSRyXUH2Tki4Pzoh7rDDAc3CSlve7fch9uuRS9L08wbcbx1g1SeuG2i7Oh HqJR4jTOWTiK9eXFHrKH5c2U++logXjTnNa+IKh0= Message-ID: <1575936329.31378.51.camel@HansenPartnership.com> Subject: [PATCH v2 1/8] security: keys: trusted: flush the key handle after use From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:05:29 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org The trusted keys code currently loads a blob into the TPM and unseals on the handle. However, it never flushes the handle meaning that volatile contexts build up until the TPM becomes unusable. Fix this by flushing the handle after the unseal. Signed-off-by: James Bottomley --- v2: use EXPORT_SYMBOL_GPL --- drivers/char/tpm/tpm.h | 1 - drivers/char/tpm/tpm2-cmd.c | 1 + include/linux/tpm.h | 1 + security/keys/trusted-keys/trusted_tpm2.c | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index b9e1547be6b5..5620747da0cf 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -218,7 +218,6 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); -void tpm2_flush_context(struct tpm_chip *chip, u32 handle); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index fdb457704aa7..13696deceae8 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -362,6 +362,7 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_transmit_cmd(chip, &buf, 0, "flushing context"); tpm_buf_destroy(&buf); } +EXPORT_SYMBOL_GPL(tpm2_flush_context); struct tpm2_get_cap_out { u8 more_data; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 0d6e949ba315..03e9b184411b 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -403,6 +403,7 @@ extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen); extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max); extern struct tpm_chip *tpm_default_chip(void); +void tpm2_flush_context(struct tpm_chip *chip, u32 handle); #else static inline int tpm_is_tpm2(struct tpm_chip *chip) { diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index a9810ac2776f..08ec7f48f01d 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -309,6 +309,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, return rc; rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); + tpm2_flush_context(chip, blob_handle); return rc; } From patchwork Tue Dec 10 00:06:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281073 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 96754138C for ; Tue, 10 Dec 2019 00:06:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 614CC2073D for ; Tue, 10 Dec 2019 00:06:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="pe7xqabd"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="pe7xqabd" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726592AbfLJAGJ (ORCPT ); Mon, 9 Dec 2019 19:06:09 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:37894 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAGJ (ORCPT ); Mon, 9 Dec 2019 19:06:09 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id A172F8EE112; Mon, 9 Dec 2019 16:06:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936368; bh=HJJZ16cSNLECi53CN7/LIHV049R0DPCqGNxVX3vxOQs=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=pe7xqabdVZjJwWFRyMKHczbRbBIt3tE3WQ4AReRZuKRo8rJSi8HS3WoqUhS2rGMWF /MfjU/halONMGK3bwmdBpMn3sOBOP/rM+k4hLuWnNgi1R3qwPzrKfGrtdDgWG5cW1X k7TNHbU0yLuGoAHcAft5GrEQbzVPQFSGEgoE5pWo= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xCd9T2sTXf9r; Mon, 9 Dec 2019 16:06:08 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 318768EE0FC; Mon, 9 Dec 2019 16:06:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936368; bh=HJJZ16cSNLECi53CN7/LIHV049R0DPCqGNxVX3vxOQs=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=pe7xqabdVZjJwWFRyMKHczbRbBIt3tE3WQ4AReRZuKRo8rJSi8HS3WoqUhS2rGMWF /MfjU/halONMGK3bwmdBpMn3sOBOP/rM+k4hLuWnNgi1R3qwPzrKfGrtdDgWG5cW1X k7TNHbU0yLuGoAHcAft5GrEQbzVPQFSGEgoE5pWo= Message-ID: <1575936367.31378.52.camel@HansenPartnership.com> Subject: [PATCH v2 2/8] lib: add asn.1 encoder From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:06:07 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org We have a need in the TPM trusted keys to return the ASN.1 form of the TPM key blob so it can be operated on by tools outside of the kernel. To do that, we have to be able to read and write the key format. The current ASN.1 decoder does fine for reading, but we need pieces of an ASN.1 encoder to return the key blob. The current implementation only encodes the ASN.1 bits we actually need. Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- v2: updated API to use indefinite length, and made symbol exports gpl --- include/linux/asn1_encoder.h | 21 ++++ lib/Makefile | 2 +- lib/asn1_encoder.c | 258 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 include/linux/asn1_encoder.h create mode 100644 lib/asn1_encoder.c diff --git a/include/linux/asn1_encoder.h b/include/linux/asn1_encoder.h new file mode 100644 index 000000000000..9cfb8035dc46 --- /dev/null +++ b/include/linux/asn1_encoder.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _LINUX_ASN1_ENCODER_H +#define _LINUX_ASN1_ENCODER_H + +#include +#include +#include + +#define asn1_oid_len(oid) (sizeof(oid)/sizeof(u32)) +void asn1_encode_integer(unsigned char **_data, s64 integer, int len); +void asn1_encode_oid(unsigned char **_data, u32 oid[], int oid_len); +void asn1_encode_tag(unsigned char **data, u32 tag, + const unsigned char *string, int len); +void asn1_encode_octet_string(unsigned char **data, const unsigned char *string, + u32 len); +void asn1_encode_sequence(unsigned char **data, const unsigned char *seq, + int len); +void asn1_encode_boolean(unsigned char **data, bool val); + +#endif diff --git a/lib/Makefile b/lib/Makefile index c2f0e2a4e4e8..515b35f92c3c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -233,7 +233,7 @@ obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o obj-$(CONFIG_PERCPU_TEST) += percpu_test.o -obj-$(CONFIG_ASN1) += asn1_decoder.o +obj-$(CONFIG_ASN1) += asn1_decoder.o asn1_encoder.o obj-$(CONFIG_FONT_SUPPORT) += fonts/ diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c new file mode 100644 index 000000000000..768cabf8bf76 --- /dev/null +++ b/lib/asn1_encoder.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Simple encoder primitives for ASN.1 BER/DER/CER + * + * Copyright (C) 2019 James.Bottomley@HansenPartnership.com + */ + +#include +#include +#include +#include + +/** + * asn1_encode_integer - encode positive integer to ASN.1 + * @_data: pointer to the pointer to the data + * @integer: integer to be encoded + * @len: length of buffer + * + * This is a simplified encoder: it only currently does + * positive integers, but it should be simple enough to add the + * negative case if a use comes along. + */ +void asn1_encode_integer(unsigned char **_data, s64 integer, int len) +{ + unsigned char *data = *_data, *d = &data[2]; + int i; + bool found = false; + + if (WARN(integer < 0, + "BUG: asn1_encode_integer only supports positive integers")) + return; + + if (WARN(len < 3, + "BUG: buffer for integers must have at least 3 bytes")) + return; + + len =- 2; + + data[0] = _tag(UNIV, PRIM, INT); + if (integer == 0) { + *d++ = 0; + goto out; + } + for (i = sizeof(integer); i > 0 ; i--) { + int byte = integer >> (8*(i-1)); + + if (!found && byte == 0) + continue; + found = true; + if (byte & 0x80) { + /* + * no check needed here, we already know we + * have len >= 1 + */ + *d++ = 0; + len--; + } + if (WARN(len == 0, + "BUG buffer too short in asn1_encode_integer")) + return; + *d++ = byte; + len--; + } + out: + data[1] = d - data - 2; + *_data = d; +} +EXPORT_SYMBOL_GPL(asn1_encode_integer); + +/* calculate the base 128 digit values setting the top bit of the first octet */ +static void asn1_encode_oid_digit(unsigned char **_data, u32 oid) +{ + int start = 7 + 7 + 7 + 7; + unsigned char *data = *_data; + + /* quick case */ + if (oid == 0) { + *data++ = 0x80; + goto out; + } + + while (oid >> start == 0) + start -= 7; + + while (start > 0) { + u8 byte; + + byte = oid >> start; + oid = oid - (byte << start); + start -= 7; + byte |= 0x80; + *data++ = byte; + } + *data++ = oid; + + out: + *_data = data; +} + +/** + * asn1_encode_oid - encode an oid to ASN.1 + * @_data: position to begin encoding at + * @oid: array of oids + * @oid_len: length of oid array + * + * this encodes an OID up to ASN.1 when presented as an array of OID values + */ +void asn1_encode_oid(unsigned char **_data, u32 oid[], int oid_len) +{ + unsigned char *data = *_data; + unsigned char *d = data + 2; + int i; + + if (WARN(oid_len < 2, "OID must have at least two elements")) + return; + if (WARN(oid_len > 32, "OID is too large")) + return; + + data[0] = _tag(UNIV, PRIM, OID); + *d++ = oid[0] * 40 + oid[1]; + for (i = 2; i < oid_len; i++) + asn1_encode_oid_digit(&d, oid[i]); + data[1] = d - data - 2; + *_data = d; +} +EXPORT_SYMBOL_GPL(asn1_encode_oid); + +static void asn1_encode_length(unsigned char **data, int len) +{ + if (len < 0) { + *((*data)++) = ASN1_INDEFINITE_LENGTH; + return; + } + if (len <= 0x7f) { + *((*data)++) = len; + return; + } + if (len <= 0xff) { + *((*data)++) = 0x81; + *((*data)++) = len & 0xff; + return; + } + if (len <= 0xffff) { + *((*data)++) = 0x82; + *((*data)++) = (len >> 8) & 0xff; + *((*data)++) = len & 0xff; + return; + } + + if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff")) + return; + + *((*data)++) = 0x83; + *((*data)++) = (len >> 16) & 0xff; + *((*data)++) = (len >> 8) & 0xff; + *((*data)++) = len & 0xff; +} + +/** + * asn1_encode_tag - add a tag for optional or explicit value + * @data: pointer to place tag at + * @tag: tag to be placed + * @string: the data to be tagged + * @len: the length of the data to be tagged + * + * Note this currently only handles short form tags < 31. To encode + * in place pass a NULL @string and -1 for @len; all this will do is + * add an indefinite length tag and update the data pointer to the + * place where the tag contents should be placed. After the data is + * placed, repeat the prior statement but now with the known length. + * In order to avoid having to keep both before and after pointers, + * the repeat expects to be called with @data pointing to where the + * first encode placed it. + */ +void asn1_encode_tag(unsigned char **data, u32 tag, + const unsigned char *string, int len) +{ + if (WARN(tag > 30, "ASN.1 tag can't be > 30")) + return; + + if (!string && WARN(len > 127, + "BUG: recode tag is too big (>127)")) + return; + + if (!string && len > 0) + /* we're recoding, so move back to the start of the tag */ + *data -= 2; + + *((*data)++) = _tagn(CONT, CONS, tag); + asn1_encode_length(data, len); + if (!string) + return; + memcpy(*data, string, len); + *data += len; +} +EXPORT_SYMBOL_GPL(asn1_encode_tag); + +/** + * asn1_encode_octet_string - encode an ASN.1 OCTET STRING + * @data: pointer to encode at + * @string: string to be encoded + * @len: length of string + * + * Note ASN.1 octet strings may contain zeros, so the length is obligatory. + */ +void asn1_encode_octet_string(unsigned char **data, const unsigned char *string, + u32 len) +{ + *((*data)++) = _tag(UNIV, PRIM, OTS); + asn1_encode_length(data, len); + memcpy(*data, string, len); + *data += len; +} +EXPORT_SYMBOL_GPL(asn1_encode_octet_string); + +/** + * asn1_encode_sequence - wrap a byte stream in an ASN.1 SEQUENCE + * @data: pointer to encode at + * @seq: data to be encoded as a sequence + * @len: length of the data to be encoded as a sequence + * + * Fill in a sequence. To encode in place, pass NULL for @seq and -1 + * for @len; then call again once the length is known (still with NULL + * for @seq). In order to avoid having to keep both before and after + * pointers, the repeat expects to be called with @data pointing to + * where the first encode placed it. + */ +void asn1_encode_sequence(unsigned char **data, const unsigned char *seq, + int len) +{ + if (!seq && WARN(len > 127, + "BUG: recode sequence is too big (>127)")) + return; + if (!seq && len > 0) + /* we're recoding, so move back to the start of the sequence */ + *data -= 2; + + *((*data)++) = _tag(UNIV, CONS, SEQ); + asn1_encode_length(data, len); + if (!seq) + return; + memcpy(*data, seq, len); + *data += len; +} +EXPORT_SYMBOL_GPL(asn1_encode_sequence); + +/** + * asn1_encode_boolean - encode a boolean value to ASN.1 + * @data: pointer to encode at + * @val: the boolean true/false value + */ +void asn1_encode_boolean(unsigned char **data, bool val) +{ + *((*data)++) = _tag(UNIV, PRIM, BOOL); + asn1_encode_length(data, 1); + *((*data)++) = val ? 1 : 0; +} +EXPORT_SYMBOL_GPL(asn1_encode_boolean); From patchwork Tue Dec 10 00:06:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281075 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E1179138C for ; Tue, 10 Dec 2019 00:06:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id BF41F2071E for ; Tue, 10 Dec 2019 00:06:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="d4ADc2Ft"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="d4ADc2Ft" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727435AbfLJAGt (ORCPT ); Mon, 9 Dec 2019 19:06:49 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:37938 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAGs (ORCPT ); Mon, 9 Dec 2019 19:06:48 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id D43ED8EE18E; Mon, 9 Dec 2019 16:06:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936407; bh=4FmAxKQz7ECJGSh7N2RP7M0+Pg0ss+HhRBx0TU9I6GY=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=d4ADc2FtQp6KfDa8rDPIr2ZsQe5uDA1lS8/3PLYFZx7Lt1izohpsDgINhomdal8/q mLUzJ1w95A6Untc2yS3TLbmgMOv8JVKcsEA7AcHwRZ2c5GUfDULNfPp0OL2eISGMzp I30+rUN07X5cqcFxFvEawf8rU+jtcvYWMB3f0Oc4= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xyFq5MdwQ-pw; Mon, 9 Dec 2019 16:06:47 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 4DFFC8EE0FC; Mon, 9 Dec 2019 16:06:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936407; bh=4FmAxKQz7ECJGSh7N2RP7M0+Pg0ss+HhRBx0TU9I6GY=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=d4ADc2FtQp6KfDa8rDPIr2ZsQe5uDA1lS8/3PLYFZx7Lt1izohpsDgINhomdal8/q mLUzJ1w95A6Untc2yS3TLbmgMOv8JVKcsEA7AcHwRZ2c5GUfDULNfPp0OL2eISGMzp I30+rUN07X5cqcFxFvEawf8rU+jtcvYWMB3f0Oc4= Message-ID: <1575936406.31378.53.camel@HansenPartnership.com> Subject: [PATCH v2 3/8] oid_registry: Add TCG defined OIDS for TPM keys From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:06:46 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org The TCG has defined an OID prefix "2.23.133.10.1" for the various TPM key uses. We've defined three of the available numbers: 2.23.133.10.1.3 TPM Loadable key. This is an asymmetric key (Usually RSA2048 or Elliptic Curve) which can be imported by a TPM2_Load() operation. 2.23.133.10.1.4 TPM Importable Key. This is an asymmetric key (Usually RSA2048 or Elliptic Curve) which can be imported by a TPM2_Import() operation. Both loadable and importable keys are specific to a given TPM, the difference is that a loadable key is wrapped with the symmetric secret, so must have been created by the TPM itself. An importable key is wrapped with a DH shared secret, and may be created without access to the TPM provided you know the public part of the parent key. 2.23.133.10.1.5 TPM Sealed Data. This is a set of data (up to 128 bytes) which is sealed by the TPM. It usually represents a symmetric key and must be unsealed before use. Signed-off-by: James Bottomley --- include/linux/oid_registry.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 657d6bf2c064..a4cee888f9b0 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -107,6 +107,11 @@ enum OID { OID_gostTC26Sign512B, /* 1.2.643.7.1.2.1.2.2 */ OID_gostTC26Sign512C, /* 1.2.643.7.1.2.1.2.3 */ + /* TCG defined OIDS for TPM based keys */ + OID_TPMLoadableKey, /* 2.23.133.10.1.3 */ + OID_TPMImporableKey, /* 2.23.133.10.1.4 */ + OID_TPMSealedData, /* 2.23.133.10.1.5 */ + OID__NR }; From patchwork Tue Dec 10 00:07:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281077 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0AE6414B7 for ; Tue, 10 Dec 2019 00:07:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C40EF2073D for ; Tue, 10 Dec 2019 00:07:33 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="VJB3V7dR"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="TjTP/l21" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727091AbfLJAHd (ORCPT ); Mon, 9 Dec 2019 19:07:33 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:37986 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAHd (ORCPT ); Mon, 9 Dec 2019 19:07:33 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 07BCB8EE112; Mon, 9 Dec 2019 16:07:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936453; bh=GgfynOpRQRXogDBFZ1EQxy/Ngs4HkeChvwp8iw6AHNw=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=VJB3V7dRGzRF+GDdXuwm3MtGrEWIQRlvPj+3qowANSRGX6elWcZ8SZG/smXBsGD8w cH9jo6hbySIa1LYEPd+1S1zKUKV1+7vyEuvs/O9IeuIFBhaHLV1B3fb3ZLEYymvufk md8fH0T9iB25eB4bn67/fut7pWUbV+6RPst5tD2Q= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id wjpsM6kmcqGc; Mon, 9 Dec 2019 16:07:32 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 8DA878EE0FC; Mon, 9 Dec 2019 16:07:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936452; bh=GgfynOpRQRXogDBFZ1EQxy/Ngs4HkeChvwp8iw6AHNw=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=TjTP/l21zNR0TEtq6cllbqWKSvCtXlcFpukZDIBpxQpi8BbFLndjAMH2OYv0tg1Q4 VX+X57/UfgFVL7Aok2xN9Y+sCc9PLUox+Un2YGw9AjRFa8BM1LQUMbEMoIc5YclEDc GJTk/h/V3NotlAmVOu0coHLbcVSBLJ4cZK9T+0cQ= Message-ID: <1575936451.31378.54.camel@HansenPartnership.com> Subject: [PATCH v2 4/8] security: keys: trusted: use ASN.1 tpm2 key format for the blobs From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:07:31 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org Modify the tpm2 key format blob output to export and import in the ASN.1 form for tpm2 sealed object keys. For compatibility with prior trusted keys, the importer will also accept two tpm2b quantities representing the public and private parts of the key. However, the export via keyctl pipe will only output the ASN.1 format. The benefit of the ASN.1 format is that it's a standard and thus the exported key can be used by userspace tools. The format includes policy specifications, thus it gets us out of having to construct policy handles in userspace and the format includes the parent meaning you don't have to keep passing it in each time. This patch only implements basic handling for the ASN.1 format, so keys with passwords but no policy. Signed-off-by: James Bottomley v2: Updated encode API, added length checks --- security/keys/trusted-keys/Makefile | 2 +- security/keys/trusted-keys/tpm2key.asn1 | 23 ++++ security/keys/trusted-keys/trusted_tpm1.c | 2 +- security/keys/trusted-keys/trusted_tpm2.c | 181 +++++++++++++++++++++++++++++- 4 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 security/keys/trusted-keys/tpm2key.asn1 diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile index 7b73cebbb378..e0198641eff2 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o trusted-y += trusted_tpm1.o -trusted-y += trusted_tpm2.o +trusted-y += trusted_tpm2.o tpm2key.asn1.o diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1 new file mode 100644 index 000000000000..1851b7c80f08 --- /dev/null +++ b/security/keys/trusted-keys/tpm2key.asn1 @@ -0,0 +1,23 @@ +--- +--- Note: This isn't quite the definition in the standard +--- However, the Linux asn.1 parser doesn't understand +--- [2] EXPLICIT SEQUENCE OF OPTIONAL +--- So there's an extra intermediate TPMPolicySequence +--- definition to work around this + +TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER ({tpmkey_type}), + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + policy [1] EXPLICIT TPMPolicySequence OPTIONAL, + secret [2] EXPLICIT OCTET STRING OPTIONAL, + parent INTEGER ({tpmkey_parent}), + pubkey OCTET STRING ({tpmkey_pub}), + privkey OCTET STRING ({tpmkey_priv}) + } + +TPMPolicySequence ::= SEQUENCE OF TPMPolicy + +TPMPolicy ::= SEQUENCE { + commandCode [0] EXPLICIT INTEGER, + commandPolicy [1] EXPLICIT OCTET STRING + } diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index d2c5ec1e040b..d744a0d1cb89 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -991,7 +991,7 @@ static int trusted_instantiate(struct key *key, goto out; } - if (!options->keyhandle) { + if (!options->keyhandle && !tpm2) { ret = -EINVAL; goto out; } diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 08ec7f48f01d..6ae3197f767f 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -4,6 +4,8 @@ * Copyright (C) 2014 Intel Corporation */ +#include +#include #include #include #include @@ -12,6 +14,10 @@ #include #include +#include + +#include "tpm2key.asn1.h" + static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, {HASH_ALGO_SHA256, TPM_ALG_SHA256}, @@ -20,6 +26,152 @@ static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, }; +static u32 tpm2key_oid[] = { 2,23,133,10,1,5 }; + +static int tpm2_key_encode(struct trusted_key_payload *payload, + struct trusted_key_options *options, + u8 *src, u32 len) +{ + const int SCRATCH_SIZE = PAGE_SIZE; + u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL); + u8 *work = scratch, *work1; + u8 *priv, *pub; + u16 priv_len, pub_len; + + priv_len = get_unaligned_be16(src); + src += 2; + priv = src; + src += priv_len; + pub_len = get_unaligned_be16(src); + src += 2; + pub = src; + + if (!scratch) + return -ENOMEM; + + asn1_encode_oid(&work, tpm2key_oid, asn1_oid_len(tpm2key_oid)); + if (options->blobauth[0] == 0) { + unsigned char bool[3], *w = bool; + /* tag 0 is emptyAuth */ + asn1_encode_boolean(&w, true); + asn1_encode_tag(&work, 0, bool, w - bool); + } + asn1_encode_integer(&work, options->keyhandle, + work - scratch + SCRATCH_SIZE); + /* + * Assume both ovtet strings will encode to a 2 byte definite length + * + * Note: For a well behaved TPM, this warning should never + * trigger, so if it does there's something nefarious going on + */ + if (WARN(work - scratch + pub_len + priv_len + 8 > SCRATCH_SIZE, + "BUG: scratch buffer is too small")) + return -EINVAL; + asn1_encode_octet_string(&work, pub, pub_len); + asn1_encode_octet_string(&work, priv, priv_len); + + work1 = payload->blob; + asn1_encode_sequence(&work1, scratch, work - scratch); + + return work1 - payload->blob; +} + +struct tpm2key_context { + u32 parent; + const u8 *pub; + u32 pub_len; + const u8 *priv; + u32 priv_len; +}; + +static int tpm2_key_decode(struct trusted_key_payload *payload, + struct trusted_key_options *options, + u8 **buf) +{ + int ret; + struct tpm2key_context ctx; + u8 *blob; + + ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, + payload->blob_len); + if (ret < 0) + return ret; + + if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE) + return -EINVAL; + + blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL); + if (!blob) + return -ENOMEM; + + *buf = blob; + options->keyhandle = ctx.parent; + put_unaligned_be16(ctx.priv_len, blob); + blob += 2; + memcpy(blob, ctx.priv, ctx.priv_len); + blob += ctx.priv_len; + put_unaligned_be16(ctx.pub_len, blob); + blob += 2; + memcpy(blob, ctx.pub, ctx.pub_len); + + return 0; +} + +int tpmkey_parent(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2key_context *ctx = context; + const u8 *v = value; + int i; + + ctx->parent = 0; + for (i = 0; i < vlen; i++) { + ctx->parent <<= 8; + ctx->parent |= v[i]; + } + return 0; +} + +int tpmkey_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid = look_up_OID(value, vlen); + + if (oid != OID_TPMSealedData) { + char buffer[50]; + + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("OID is \"%s\" which is not TPMSealedData\n", + buffer); + return -EINVAL; + } + return 0; +} + +int tpmkey_pub(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2key_context *ctx = context; + + ctx->pub = value; + ctx->pub_len = vlen; + return 0; +} + +int tpmkey_priv(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2key_context *ctx = context; + + ctx->priv = value; + ctx->priv_len = vlen; + return 0; +} + /** * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * @@ -79,6 +231,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (i == ARRAY_SIZE(tpm2_hash_map)) return -EINVAL; + if (!options->keyhandle) + return -EINVAL; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) return rc; @@ -144,8 +299,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); - payload->blob_len = blob_len; + payload->blob_len = + tpm2_key_encode(payload, options, + &buf.data[TPM_HEADER_SIZE + 4], + blob_len); out: tpm_buf_destroy(&buf); @@ -156,6 +313,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip, else rc = -EPERM; } + if (payload->blob_len < 0) + return payload->blob_len; return rc; } @@ -182,13 +341,23 @@ static int tpm2_load_cmd(struct tpm_chip *chip, unsigned int private_len; unsigned int public_len; unsigned int blob_len; + u8 *blob; int rc; - private_len = be16_to_cpup((__be16 *) &payload->blob[0]); + rc = tpm2_key_decode(payload, options, &blob); + if (rc) + /* old form */ + blob = payload->blob; + + /* new format carries keyhandle but old format doesn't */ + if (!options->keyhandle) + return -EINVAL; + + private_len = be16_to_cpup((__be16 *) &blob[0]); if (private_len > (payload->blob_len - 2)) return -E2BIG; - public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); + public_len = be16_to_cpup((__be16 *) &blob[2 + private_len]); blob_len = private_len + public_len + 4; if (blob_len > payload->blob_len) return -E2BIG; @@ -204,7 +373,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, options->keyauth /* hmac */, TPM_DIGEST_SIZE); - tpm_buf_append(&buf, payload->blob, blob_len); + tpm_buf_append(&buf, blob, blob_len); if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; @@ -217,6 +386,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip, (__be32 *) &buf.data[TPM_HEADER_SIZE]); out: + if (blob != payload->blob) + kfree(blob); tpm_buf_destroy(&buf); if (rc > 0) From patchwork Tue Dec 10 00:08:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281079 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 40F5C138C for ; Tue, 10 Dec 2019 00:08:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0E5622071E for ; Tue, 10 Dec 2019 00:08:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="TBBVB0S5"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="TBBVB0S5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727276AbfLJAIL (ORCPT ); Mon, 9 Dec 2019 19:08:11 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:38034 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAIL (ORCPT ); Mon, 9 Dec 2019 19:08:11 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 55F878EE112; Mon, 9 Dec 2019 16:08:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936491; bh=Sv+dYY144NbR3Y+mnbT1AlgQKmRsu3svS/dGn4iv9sU=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=TBBVB0S59w0dTklPxSMRViekyJXbut8FamWqDp/r8Fxj/2F7XeGCVHWRvP4oBIEsS dZhgJpI4bDajH019ulP1wREUGAPPNQIS/HBMN8f9LL+aZ18j2BR1VUGs1DiffpoNR2 p/VN1CR2R+Xcz16q0m40v6oYKDAdKusf04dHBvSU= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dFy4rh2-2svk; Mon, 9 Dec 2019 16:08:11 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id EC1748EE0FC; Mon, 9 Dec 2019 16:08:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936491; bh=Sv+dYY144NbR3Y+mnbT1AlgQKmRsu3svS/dGn4iv9sU=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=TBBVB0S59w0dTklPxSMRViekyJXbut8FamWqDp/r8Fxj/2F7XeGCVHWRvP4oBIEsS dZhgJpI4bDajH019ulP1wREUGAPPNQIS/HBMN8f9LL+aZ18j2BR1VUGs1DiffpoNR2 p/VN1CR2R+Xcz16q0m40v6oYKDAdKusf04dHBvSU= Message-ID: <1575936490.31378.55.camel@HansenPartnership.com> Subject: [PATCH v2 5/8] security: keys: trusted: Make sealed key properly interoperable From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:08:10 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org The current implementation appends a migratable flag to the end of a key, meaning the format isn't exactly interoperable because the using party needs to know to strip this extra byte. However, all other consumers of TPM sealed blobs expect the unseal to return exactly the key. Since TPM2 keys have a key property flag that corresponds to migratable, use that flag instead and make the actual key the only sealed quantity. This is secure because the key properties are bound to a hash in the private part, so if they're altered the key won't load. Backwards compatibility is implemented by detecting whether we're loading a new format key or not and correctly setting migratable from the last byte of old format keys. Signed-off-by: James Bottomley --- v2: added length checks to untrusted payload --- include/keys/trusted-type.h | 1 + include/linux/tpm.h | 2 + security/keys/trusted-keys/trusted_tpm2.c | 69 ++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index a94c03a61d8f..4728e13aada8 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -22,6 +22,7 @@ struct trusted_key_payload { unsigned int key_len; unsigned int blob_len; unsigned char migratable; + unsigned char old_format; unsigned char key[MAX_KEY_SIZE + 1]; unsigned char blob[MAX_BLOB_SIZE]; }; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 03e9b184411b..cd46ab27baa5 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -297,6 +297,8 @@ struct tpm_buf { }; enum tpm2_object_attributes { + TPM2_OA_FIXED_TPM = BIT(1), + TPM2_OA_FIXED_PARENT = BIT(4), TPM2_OA_USER_WITH_AUTH = BIT(6), }; diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 6ae3197f767f..6d2c5adbff18 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -218,6 +218,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, unsigned int blob_len; struct tpm_buf buf; u32 hash; + u32 flags; int i; int rc; @@ -246,29 +247,30 @@ int tpm2_seal_trusted(struct tpm_chip *chip, TPM_DIGEST_SIZE); /* sensitive */ - tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); + tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len); tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); - tpm_buf_append_u16(&buf, payload->key_len + 1); + tpm_buf_append_u16(&buf, payload->key_len); tpm_buf_append(&buf, payload->key, payload->key_len); - tpm_buf_append_u8(&buf, payload->migratable); /* public */ tpm_buf_append_u16(&buf, 14 + options->policydigest_len); tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); tpm_buf_append_u16(&buf, hash); + /* key properties */ + flags = 0; + flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; + flags |= payload->migratable ? (TPM2_OA_FIXED_TPM | + TPM2_OA_FIXED_PARENT) : 0; + tpm_buf_append_u32(&buf, flags); + /* policy */ - if (options->policydigest_len) { - tpm_buf_append_u32(&buf, 0); - tpm_buf_append_u16(&buf, options->policydigest_len); + tpm_buf_append_u16(&buf, options->policydigest_len); + if (options->policydigest_len) tpm_buf_append(&buf, options->policydigest, options->policydigest_len); - } else { - tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); - } /* public parameters */ tpm_buf_append_u16(&buf, TPM_ALG_NULL); @@ -341,23 +343,42 @@ static int tpm2_load_cmd(struct tpm_chip *chip, unsigned int private_len; unsigned int public_len; unsigned int blob_len; - u8 *blob; + u8 *blob, *pub; int rc; + u32 attrs; rc = tpm2_key_decode(payload, options, &blob); - if (rc) + if (rc) { /* old form */ blob = payload->blob; + payload->old_format = 1; + } /* new format carries keyhandle but old format doesn't */ if (!options->keyhandle) return -EINVAL; - private_len = be16_to_cpup((__be16 *) &blob[0]); - if (private_len > (payload->blob_len - 2)) + /* must be big enough for at least the two be16 size counts */ + if (payload->blob_len < 4) + return -EINVAL; + private_len = get_unaligned_be16(blob); + /* must be big enough for following public_len */ + if (private_len + 2 + 2 > (payload->blob_len)) + return -E2BIG; + + public_len = get_unaligned_be16(blob + 2 + private_len); + if (private_len + 2 + public_len + 2 > payload->blob_len) return -E2BIG; - public_len = be16_to_cpup((__be16 *) &blob[2 + private_len]); + pub = blob + 2 + private_len + 2; + /* key attributes are always at offset 4 */ + attrs = get_unaligned_be32(pub + 4); + if ((attrs & (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) == + (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) + payload->migratable = 0; + else + payload->migratable = 1; + blob_len = private_len + public_len + 4; if (blob_len > payload->blob_len) return -E2BIG; @@ -438,7 +459,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, if (!rc) { data_len = be16_to_cpup( (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { + if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) { rc = -EFAULT; goto out; } @@ -449,9 +470,19 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, } data = &buf.data[TPM_HEADER_SIZE + 6]; - memcpy(payload->key, data, data_len - 1); - payload->key_len = data_len - 1; - payload->migratable = data[data_len - 1]; + if (payload->old_format) { + /* migratable flag is at the end of the key */ + memcpy(payload->key, data, data_len - 1); + payload->key_len = data_len - 1; + payload->migratable = data[data_len - 1]; + } else { + /* + * migratable flag already collected from key + * attributes + */ + memcpy(payload->key, data, data_len); + payload->key_len = data_len; + } } out: From patchwork Tue Dec 10 00:08:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281095 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D218714B7 for ; Tue, 10 Dec 2019 00:08:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 93AE42077B for ; Tue, 10 Dec 2019 00:08:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="csdEmyko"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="csdEmyko" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727276AbfLJAI7 (ORCPT ); Mon, 9 Dec 2019 19:08:59 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:38076 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAI7 (ORCPT ); Mon, 9 Dec 2019 19:08:59 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id E41FF8EE112; Mon, 9 Dec 2019 16:08:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936538; bh=bMk8CTjW8BI82I6Vmy5MVN+2BUXJmqnz6MJPNjvkQew=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=csdEmykoSGpsyFzlqAYF9xG9FufiHvLuandKwCCMQyf4YJnz04GOTIHb8qgV0HtJF GSl1wNn777u5oLXLGmTTRYSsDQtG/oHDa26LGHCd8k+tznHvq5pyY0jyqMLJZEi4AR d6nO/pccgpM3DRV5yIqXpAVh4r0QkGH80y6Irk0c= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id usj9UxFbrbDY; Mon, 9 Dec 2019 16:08:58 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 795228EE0FC; Mon, 9 Dec 2019 16:08:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936538; bh=bMk8CTjW8BI82I6Vmy5MVN+2BUXJmqnz6MJPNjvkQew=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=csdEmykoSGpsyFzlqAYF9xG9FufiHvLuandKwCCMQyf4YJnz04GOTIHb8qgV0HtJF GSl1wNn777u5oLXLGmTTRYSsDQtG/oHDa26LGHCd8k+tznHvq5pyY0jyqMLJZEi4AR d6nO/pccgpM3DRV5yIqXpAVh4r0QkGH80y6Irk0c= Message-ID: <1575936537.31378.56.camel@HansenPartnership.com> Subject: [PATCH v2 6/8] security: keys: trusted: add PCR policy to TPM2 keys From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:08:57 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org This commit adds the ability to specify a PCR lock policy to TPM2 keys. There is a complexity in that the creator of the key must chose either to use a PCR lock policy or to use authentication. At the current time they can't use both due to a complexity with the way authentication works when policy registers are in use. The way to construct a pcrinfo statement for a key is simply to use the TPMS_PCR_SELECT structure to specify the PCRs and follow this by a hash of all their values in order of ascending PCR number. For simplicity, we require the policy name hash and the hash used for the PCRs to be the same. Thus to construct a policy around the value of the resettable PCR 16 using the sha1 bank, first reset the pcr to zero giving a hash of all zeros as: 6768033e216468247bd031a0a2d9876d79818f8f Then the TPMS_PCR_SELECT value for PCR 16 is 03000001 So create a new 32 byte key with a policy policy locking the key to this value of PCR 16 with a parent key of 81000001 would be: keyctl new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u Signed-off-by: James Bottomley --- v2: fix for new ASN.1 API eliminating hack in place and check lengths --- Documentation/security/keys/trusted-encrypted.rst | 25 +- include/keys/trusted-type.h | 5 +- include/linux/tpm.h | 5 + security/keys/Kconfig | 2 + security/keys/trusted-keys/Makefile | 2 +- security/keys/trusted-keys/tpm2-policy.c | 340 ++++++++++++++++++++++ security/keys/trusted-keys/tpm2-policy.h | 30 ++ security/keys/trusted-keys/tpm2key.asn1 | 4 +- security/keys/trusted-keys/trusted_tpm1.c | 32 +- security/keys/trusted-keys/trusted_tpm2.c | 86 +++++- 10 files changed, 480 insertions(+), 51 deletions(-) create mode 100644 security/keys/trusted-keys/tpm2-policy.c create mode 100644 security/keys/trusted-keys/tpm2-policy.h diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index 50ac8bcd6970..1a3ca84ad3cd 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -60,19 +60,16 @@ Usage:: (40 ascii zeros) blobauth= ascii hex auth for sealed data default 0x00... (40 ascii zeros) - pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) + pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no + default) on TPM 1.2 and a TPMS_PCR_SELECTION + coupled with a hash of all the selected PCRs on + TPM 2.0 using the selected hash. pcrlock= pcr number to be extended to "lock" blob migratable= 0|1 indicating permission to reseal to new PCR values, default 1 (resealing allowed) hash= hash algorithm name as a string. For TPM 1.x the only allowed value is sha1. For TPM 2.x the allowed values are sha1, sha256, sha384, sha512 and sm3-256. - policydigest= digest for the authorization policy. must be calculated - with the same hash algorithm as specified by the 'hash=' - option. - policyhandle= handle to an authorization policy session that defines the - same policy and with the same hash algorithm as was used to - seal the key. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. @@ -151,6 +148,20 @@ Load a trusted key from the saved blob:: f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b e4a8aea2b607ec96931e6f4d4fe563ba +Create a trusted key on TPM 2.0 using an all zero value of PCR16 and +using the NV storage root 81000001 as the parent:: + + $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 pcrinfo=030000016768033e216468247bd031a0a2d9876d79818f8f" @u + +Note the TPMS_PCR_SELECT value for PCR 16 is 03000001 because all +current TPMs have 24 PCRs, so the initial 03 says there are three +following bytes of selection and then because the bytes are big +endian, 16 is bit zero of byte 2. the hash is the sha1 sum of all +zeros (the value of PCR 16):: + + $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum + 6768033e216468247bd031a0a2d9876d79818f8f + Reseal a trusted key under new pcr values:: $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`" diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index 4728e13aada8..fc9c13802c06 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -14,9 +14,11 @@ #define MIN_KEY_SIZE 32 #define MAX_KEY_SIZE 128 #define MAX_BLOB_SIZE 512 -#define MAX_PCRINFO_SIZE 64 +#define MAX_PCRINFO_SIZE 128 #define MAX_DIGEST_SIZE 64 +#define TPM2_MAX_POLICIES 16 + struct trusted_key_payload { struct rcu_head rcu; unsigned int key_len; @@ -25,6 +27,7 @@ struct trusted_key_payload { unsigned char old_format; unsigned char key[MAX_KEY_SIZE + 1]; unsigned char blob[MAX_BLOB_SIZE]; + struct tpm2_policies *policies; }; struct trusted_key_options { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index cd46ab27baa5..e32e9728adce 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -222,10 +222,14 @@ enum tpm2_command_codes { TPM2_CC_CONTEXT_LOAD = 0x0161, TPM2_CC_CONTEXT_SAVE = 0x0162, TPM2_CC_FLUSH_CONTEXT = 0x0165, + TPM2_CC_POLICY_AUTHVALUE = 0x016B, + TPM2_CC_POLICY_COUNTER_TIMER = 0x016D, + TPM2_CC_START_AUTH_SESS = 0x0176, TPM2_CC_VERIFY_SIGNATURE = 0x0177, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, TPM2_CC_PCR_READ = 0x017E, + TPM2_CC_POLICY_PCR = 0x017F, TPM2_CC_PCR_EXTEND = 0x0182, TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185, TPM2_CC_HASH_SEQUENCE_START = 0x0186, @@ -234,6 +238,7 @@ enum tpm2_command_codes { }; enum tpm2_permanent_handles { + TPM2_RH_NULL = 0x40000007, TPM2_RS_PW = 0x40000009, }; diff --git a/security/keys/Kconfig b/security/keys/Kconfig index dd313438fecf..6c2f2c22b284 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -80,6 +80,8 @@ config TRUSTED_KEYS select CRYPTO select CRYPTO_HMAC select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_SHA512 select CRYPTO_HASH_INFO help This option provides support for creating, sealing, and unsealing diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile index e0198641eff2..194febacf362 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_TRUSTED_KEYS) += trusted.o trusted-y += trusted_tpm1.o -trusted-y += trusted_tpm2.o tpm2key.asn1.o +trusted-y += trusted_tpm2.o tpm2key.asn1.o tpm2-policy.o diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c new file mode 100644 index 000000000000..1c70f82ed170 --- /dev/null +++ b/security/keys/trusted-keys/tpm2-policy.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 James.Bottomley@HansenPartnership.com + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "tpm2key.asn1.h" +#include "tpm2-policy.h" + +int tpmkey_code(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2key_context *ctx = context; + u32 code = 0; + const u8 *v = value; + int i; + + for (i = 0; i < vlen; i++) { + code <<= 8; + code |= v[i]; + } + + ctx->policy_code[ctx->policy_count] = code; + + return 0; +} + +int tpmkey_policy(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct tpm2key_context *ctx = context; + + ctx->policies[ctx->policy_count] = value; + ctx->policy_len[ctx->policy_count++] = vlen; + + return 0; +} + +/* we only support a limited number of policy statement so + * make sure we don't have anything we can't support + */ +static int tpm2_validate_policy(struct tpm2_policies *pols) +{ + int i; + + if (pols->count == 0) + return 0; + + for (i = 0; i < pols->count; i++) { + switch (pols->code[i]) { + case TPM2_CC_POLICY_COUNTER_TIMER: + case TPM2_CC_POLICY_PCR: + case TPM2_CC_POLICY_AUTHVALUE: + break; + default: + printk(KERN_INFO "tpm2 policy 0x%x is unsupported", + pols->code[i]); + return -EINVAL; + } + } + return 0; +} + +/** + * tpmkey_process_policy - collect the policty from the context + * @ctx: the context to collect from + * @payload: the payload structure to place it in + * + * THis function sizes the policy statements and allocates space + * within the payload to receive them before copying them over. It + * should be used after the ber decoder has completed successfully + */ +int tpmkey_policy_process(struct tpm2key_context *ctx, + struct trusted_key_payload *payload) +{ + int tot_len = 0; + u8 *buf; + int i, ret, len = 0; + struct tpm2_policies *pols; + + if (ctx->policy_count == 0) + return 0; + + for (i = 0; i < ctx->policy_count; i++) + tot_len += ctx->policy_len[i]; + tot_len += sizeof(*pols); + + pols = kmalloc(tot_len, GFP_KERNEL); + if (!pols) + return -ENOMEM; + + payload->policies = pols; + buf = (u8 *)(pols + 1); + + for (i = 0; i < ctx->policy_count; i++) { + pols->policies[i] = &buf[len]; + pols->len[i] = ctx->policy_len[i]; + pols->code[i] = ctx->policy_code[i]; + if (pols->len[i]) + memcpy(pols->policies[i], ctx->policies[i], + ctx->policy_len[i]); + len += ctx->policy_len[i]; + } + pols->count = ctx->policy_count; + + ret = tpm2_validate_policy(pols); + if (ret) { + kfree(pols); + payload->policies = NULL; + } + + /* capture the hash and size */ + + /* the hash is the second algorithm */ + pols->hash = get_unaligned_be16(&ctx->pub[2]); + /* and the digest appears after the attributes */ + pols->hash_size = get_unaligned_be16(&ctx->pub[8]); + + return ret; +} + +int tpm2_generate_policy_digest(struct tpm2_policies *pols, + u32 hash, u8 *policydigest, u32 *plen) +{ + int i; + struct crypto_shash *tfm; + int rc; + + if (pols->count == 0) + return 0; + + tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = crypto_shash_digestsize(tfm); + if (WARN(rc > MAX_DIGEST_SIZE, + "BUG: trusted key code has alg %s with digest too large (%d)", + hash_algo_name[hash], rc)) { + rc = -EINVAL; + goto err; + } + pols->hash = hash; + pols->hash_size = rc; + *plen = rc; + + /* policy digests always start out all zeros */ + memset(policydigest, 0, rc); + + for (i = 0; i < pols->count; i++) { + u8 *policy = pols->policies[i]; + int len = pols->len[i]; + u32 cmd = pols->code[i]; + u8 digest[MAX_DIGEST_SIZE]; + u8 code[4]; + SHASH_DESC_ON_STACK(sdesc, tfm); + + sdesc->tfm = tfm; + rc = crypto_shash_init(sdesc); + if (rc) + goto err; + + /* first hash the previous digest */ + crypto_shash_update(sdesc, policydigest, *plen); + /* then hash the command code */ + put_unaligned_be32(cmd, code); + crypto_shash_update(sdesc, code, 4); + + /* commands that need special handling */ + if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) { + SHASH_DESC_ON_STACK(sdesc1, tfm); + + sdesc1->tfm = tfm; + + /* counter timer policies are double hashed */ + crypto_shash_digest(sdesc1, policy, len, + digest); + policy = digest; + len = *plen; + } + crypto_shash_update(sdesc, policy, len); + /* now output the intermediate to the policydigest */ + crypto_shash_final(sdesc, policydigest); + + } + rc = 0; + + err: + crypto_free_shash(tfm); + return rc; +} + +int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len) +{ + const int SCRATCH_SIZE = PAGE_SIZE; + u8 *buf = kmalloc(2 * SCRATCH_SIZE, GFP_KERNEL); + u8 *work = buf + SCRATCH_SIZE, *ptr; + int i; + + if (!buf) + return -ENOMEM; + + for (i = 0; i < pols->count; i++) { + u8 *seq, *tag; + u32 cmd = pols->code[i]; + + if (WARN(work - buf + 14 + pols->len[i] > 2 * SCRATCH_SIZE, + "BUG: scratch buffer is too small")) + return -EINVAL; + + asn1_encode_sequence(&work, NULL, -1); + seq = work; + + asn1_encode_tag(&work, 0, NULL, -1); + tag = work; + asn1_encode_integer(&work, cmd, + work - buf + SCRATCH_SIZE); + asn1_encode_tag(&tag, 0, NULL, work - tag); + + asn1_encode_tag(&work, 1, NULL, -1); + tag = work; + asn1_encode_octet_string(&work, pols->policies[i], + pols->len[i]); + asn1_encode_tag(&tag, 1, NULL, work - tag); + + asn1_encode_sequence(&seq, NULL, work - seq); + } + ptr = buf; + asn1_encode_sequence(&ptr, buf + PAGE_SIZE, work - buf - PAGE_SIZE); + *data = buf; + *len = ptr - buf; + + return 0; +} + +int tpm2_start_policy_session(struct tpm_chip *chip, u16 hash, u32 *handle) +{ + struct tpm_buf buf; + int rc; + int i; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS); + if (rc) + return rc; + + /* NULL salt key handle */ + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + /* NULL bind key handle */ + tpm_buf_append_u32(&buf, TPM2_RH_NULL); + /* empty nonce caller */ + tpm_buf_append_u16(&buf, 20); + for (i = 0; i < 20; i++) + tpm_buf_append_u8(&buf, 0); + /* empty auth */ + tpm_buf_append_u16(&buf, 0); + /* session type policy */ + tpm_buf_append_u8(&buf, 0x01); + + /* symmetric encryption parameters */ + /* symmetric algorithm */ + tpm_buf_append_u16(&buf, TPM_ALG_NULL); + /* hash algorithm for session */ + tpm_buf_append_u16(&buf, hash); + + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + if (rc) + goto out; + + *handle = get_unaligned_be32(buf.data + TPM_HEADER_SIZE); + out: + tpm_buf_destroy(&buf); + + return rc <= 0 ? rc : -EPERM; +} + +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols, + u32 *handle) +{ + int i, rc; + const char *failure; + + rc = tpm2_start_policy_session(chip, pols->hash, handle); + if (rc) + return rc; + + for (i = 0; i < pols->count; i++) { + u32 cmd = pols->code[i]; + struct tpm_buf buf; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, cmd); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, *handle); + + switch (cmd) { + case TPM2_CC_POLICY_PCR: + failure = "PCR"; + /* + * for reasons best known to the TCG we have + * to reverse the two arguments to send to the + * policy command + */ + tpm_buf_append_u16(&buf, pols->hash_size); + tpm_buf_append(&buf, pols->policies[i] + pols->len[i] - + pols->hash_size, pols->hash_size); + tpm_buf_append(&buf, pols->policies[i], + pols->len[i] - pols->hash_size); + break; + default: + failure = "unknown policy"; + break; + } + rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + tpm_buf_destroy(&buf); + if (rc) { + printk(KERN_NOTICE "TPM policy %s failed, rc=%d\n", + failure, rc); + tpm2_flush_context(chip, *handle); + *handle = 0; + return -EPERM; + } + } + return 0; +} diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h new file mode 100644 index 000000000000..152c948743f3 --- /dev/null +++ b/security/keys/trusted-keys/tpm2-policy.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +struct tpm2key_context { + u32 parent; + const u8 *pub; + u32 pub_len; + const u8 *priv; + u32 priv_len; + const u8 *policies[TPM2_MAX_POLICIES]; + u32 policy_code[TPM2_MAX_POLICIES]; + u16 policy_len[TPM2_MAX_POLICIES]; + u8 policy_count; +}; + +struct tpm2_policies { + u32 code[TPM2_MAX_POLICIES]; + u8 *policies[TPM2_MAX_POLICIES]; + u16 len[TPM2_MAX_POLICIES]; + u8 count; + u16 hash; + u16 hash_size; +}; + +int tpmkey_policy_process(struct tpm2key_context *ctx, + struct trusted_key_payload *payload); +int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash, + u8 *policydigest, u32 *plen); +int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len); +int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols, + u32 *handle); diff --git a/security/keys/trusted-keys/tpm2key.asn1 b/security/keys/trusted-keys/tpm2key.asn1 index 1851b7c80f08..f930fd812db3 100644 --- a/security/keys/trusted-keys/tpm2key.asn1 +++ b/security/keys/trusted-keys/tpm2key.asn1 @@ -18,6 +18,6 @@ TPMKey ::= SEQUENCE { TPMPolicySequence ::= SEQUENCE OF TPMPolicy TPMPolicy ::= SEQUENCE { - commandCode [0] EXPLICIT INTEGER, - commandPolicy [1] EXPLICIT OCTET STRING + commandCode [0] EXPLICIT INTEGER ({tpmkey_code}), + commandPolicy [1] EXPLICIT OCTET STRING ({tpmkey_policy}) } diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index d744a0d1cb89..6290e611b632 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -707,8 +707,6 @@ enum { Opt_keyhandle, Opt_keyauth, Opt_blobauth, Opt_pcrinfo, Opt_pcrlock, Opt_migratable, Opt_hash, - Opt_policydigest, - Opt_policyhandle, }; static const match_table_t key_tokens = { @@ -722,8 +720,6 @@ static const match_table_t key_tokens = { {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, {Opt_hash, "hash=%s"}, - {Opt_policydigest, "policydigest=%s"}, - {Opt_policyhandle, "policyhandle=%s"}, {Opt_err, NULL} }; @@ -738,7 +734,6 @@ static int getoptions(char *c, struct trusted_key_payload *pay, unsigned long handle; unsigned long lock; unsigned long token_mask = 0; - unsigned int digest_len; int i; int tpm2; @@ -801,8 +796,6 @@ static int getoptions(char *c, struct trusted_key_payload *pay, opt->pcrlock = lock; break; case Opt_hash: - if (test_bit(Opt_policydigest, &token_mask)) - return -EINVAL; for (i = 0; i < HASH_ALGO__LAST; i++) { if (!strcmp(args[0].from, hash_algo_name[i])) { opt->hash = i; @@ -816,24 +809,6 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; } break; - case Opt_policydigest: - digest_len = hash_digest_size[opt->hash]; - if (!tpm2 || strlen(args[0].from) != (2 * digest_len)) - return -EINVAL; - res = hex2bin(opt->policydigest, args[0].from, - digest_len); - if (res < 0) - return -EINVAL; - opt->policydigest_len = digest_len; - break; - case Opt_policyhandle: - if (!tpm2) - return -EINVAL; - res = kstrtoul(args[0].from, 16, &handle); - if (res < 0) - return -EINVAL; - opt->policyhandle = handle; - break; default: return -EINVAL; } @@ -1045,6 +1020,7 @@ static void trusted_rcu_free(struct rcu_head *rcu) struct trusted_key_payload *p; p = container_of(rcu, struct trusted_key_payload, rcu); + kzfree(p->policies); kzfree(p); } @@ -1164,7 +1140,11 @@ static long trusted_read(const struct key *key, char __user *buffer, */ static void trusted_destroy(struct key *key) { - kzfree(key->payload.data[0]); + struct trusted_key_payload *p; + + p = key->payload.data[0]; + kzfree(p->policies); + kzfree(p); } struct key_type key_type_trusted = { diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 6d2c5adbff18..ffdd5bf27ea7 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -17,6 +17,7 @@ #include #include "tpm2key.asn1.h" +#include "tpm2-policy.h" static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, @@ -56,17 +57,34 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, asn1_encode_boolean(&w, true); asn1_encode_tag(&work, 0, bool, w - bool); } - asn1_encode_integer(&work, options->keyhandle, - work - scratch + SCRATCH_SIZE); + if (payload->policies) { + u8 *encoded_pols; + u32 encoded_pol_len; + int ret; + + ret = tpm2_encode_policy(payload->policies, &encoded_pols, + &encoded_pol_len); + + if (ret) { + return ret; + } else { + asn1_encode_tag(&work, 1, encoded_pols, + encoded_pol_len); + kfree(encoded_pols); + } + } /* * Assume both ovtet strings will encode to a 2 byte definite length * * Note: For a well behaved TPM, this warning should never * trigger, so if it does there's something nefarious going on */ - if (WARN(work - scratch + pub_len + priv_len + 8 > SCRATCH_SIZE, + if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE, "BUG: scratch buffer is too small")) return -EINVAL; + + asn1_encode_integer(&work, options->keyhandle, + SCRATCH_SIZE - (work - scratch)); asn1_encode_octet_string(&work, pub, pub_len); asn1_encode_octet_string(&work, priv, priv_len); @@ -76,14 +94,6 @@ static int tpm2_key_encode(struct trusted_key_payload *payload, return work1 - payload->blob; } -struct tpm2key_context { - u32 parent; - const u8 *pub; - u32 pub_len; - const u8 *priv; - u32 priv_len; -}; - static int tpm2_key_decode(struct trusted_key_payload *payload, struct trusted_key_options *options, u8 **buf) @@ -92,6 +102,8 @@ static int tpm2_key_decode(struct trusted_key_payload *payload, struct tpm2key_context ctx; u8 *blob; + memset(&ctx, 0, sizeof(ctx)); + ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob, payload->blob_len); if (ret < 0) @@ -104,6 +116,12 @@ static int tpm2_key_decode(struct trusted_key_payload *payload, if (!blob) return -ENOMEM; + ret = tpmkey_policy_process(&ctx, payload); + if (ret) { + kfree(blob); + return ret; + } + *buf = blob; options->keyhandle = ctx.parent; put_unaligned_be16(ctx.priv_len, blob); @@ -235,6 +253,37 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (!options->keyhandle) return -EINVAL; + if (options->pcrinfo_len != 0) { + struct tpm2_policies *pols; + static u8 *scratch; + /* 4 array len, 2 hash alg */ + const int len = 4 + 2 + options->pcrinfo_len; + + pols = kmalloc(sizeof(*pols) + len, GFP_KERNEL); + if (!pols) + return -ENOMEM; + + pols->count = 1; + pols->len[0] = len; + scratch = (u8 *)(pols + 1); + pols->policies[0] = scratch; + pols->code[0] = TPM2_CC_POLICY_PCR; + + put_unaligned_be32(1, &scratch[0]); + put_unaligned_be16(hash, &scratch[4]); + memcpy(&scratch[6], options->pcrinfo, options->pcrinfo_len); + payload->policies = pols; + } + + if (payload->policies) { + rc = tpm2_generate_policy_digest(payload->policies, + options->hash, + options->policydigest, + &options->policydigest_len); + if (rc) + return rc; + } + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) return rc; @@ -438,21 +487,30 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, u16 data_len; u8 *data; int rc; + u32 policyhandle = 0; rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); if (rc) return rc; + if (payload->policies) { + rc = tpm2_get_policy_session(chip, payload->policies, + &policyhandle); + if (rc) + return rc; + } + tpm_buf_append_u32(&buf, blob_handle); tpm2_buf_append_auth(&buf, - options->policyhandle ? - options->policyhandle : TPM2_RS_PW, + policyhandle ? + policyhandle : TPM2_RS_PW, NULL /* nonce */, 0, TPM2_SA_CONTINUE_SESSION, options->blobauth /* hmac */, - TPM_DIGEST_SIZE); + policyhandle ? 0 : TPM_DIGEST_SIZE); rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + tpm2_flush_context(chip, policyhandle); if (rc > 0) rc = -EPERM; From patchwork Tue Dec 10 00:09:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281097 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 77C89138C for ; Tue, 10 Dec 2019 00:09:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4D0162077B for ; Tue, 10 Dec 2019 00:09:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="Kjq39Lbj"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="Kjq39Lbj" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727329AbfLJAJg (ORCPT ); Mon, 9 Dec 2019 19:09:36 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:38152 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAJg (ORCPT ); Mon, 9 Dec 2019 19:09:36 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 669C08EE112; Mon, 9 Dec 2019 16:09:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936576; bh=Y/4OD1VkEsU7wFQGa8fI/dht0J0LEbAMe7NTw79lPus=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=Kjq39LbjFzUaUZfiaxW3BnUIvYkAsniSgSoHxvB+3HJN6wXY7ClpqHPR894eTTNr3 Ebr5JCDDD2CtLWJ703XoB24Lv+K291m32ZuAb+Cm8oflZL1k1gYyGuNgp+0hSP+08w vTfphA+Cx/cuGKsQBddz5SbgZDcg3MKLMugL0vfU= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 54eQHXdtDMzc; Mon, 9 Dec 2019 16:09:36 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id E9BEF8EE0FC; Mon, 9 Dec 2019 16:09:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936576; bh=Y/4OD1VkEsU7wFQGa8fI/dht0J0LEbAMe7NTw79lPus=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=Kjq39LbjFzUaUZfiaxW3BnUIvYkAsniSgSoHxvB+3HJN6wXY7ClpqHPR894eTTNr3 Ebr5JCDDD2CtLWJ703XoB24Lv+K291m32ZuAb+Cm8oflZL1k1gYyGuNgp+0hSP+08w vTfphA+Cx/cuGKsQBddz5SbgZDcg3MKLMugL0vfU= Message-ID: <1575936575.31378.57.camel@HansenPartnership.com> Subject: [PATCH v2 7/8] security: keys: trusted: add ability to specify arbitrary policy From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:09:35 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org This patch adds a policy= argument to key creation. The policy is the standard tss policymaker format and each separate policy line must have a newline after it. Thus to construct a policy requiring authorized value and pcr 16 locking using a sha256 hash, the policy (policy.txt) file would be two lines: 0000017F00000001000B03000001303095B49BE85E381E5B20E557E46363EF55B0F43B132C2D8E3DE9AC436656F2 0000016b This can be inserted into the key with keyctl add trusted kmk "new 32 policy=`cat policy.txt` keyhandle=0x81000001 hash=sha256" @u Note that although a few policies work like this, most require special handling which must be added to the kernel policy construction routine. Signed-off-by: James Bottomley --- Documentation/security/keys/trusted-encrypted.rst | 16 ++++++++ security/keys/trusted-keys/tpm2-policy.c | 46 +++++++++++++++++++++++ security/keys/trusted-keys/tpm2-policy.h | 1 + security/keys/trusted-keys/trusted_tpm1.c | 14 ++++++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index 1a3ca84ad3cd..ade1a9dc8367 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -70,6 +70,9 @@ Usage:: hash= hash algorithm name as a string. For TPM 1.x the only allowed value is sha1. For TPM 2.x the allowed values are sha1, sha256, sha384, sha512 and sm3-256. + policy= specify an arbitrary set of policies. These must + be in policymaker format with each separate + policy line newline terminated. "keyctl print" returns an ascii hex copy of the sealed key, which is in standard TPM_STORED_DATA format. The key length for new keys are always in bytes. @@ -162,6 +165,19 @@ zeros (the value of PCR 16):: $ dd if=/dev/zero bs=1 count=20 2>/dev/null|sha1sum 6768033e216468247bd031a0a2d9876d79818f8f +You can also specify arbitrary policy in policymaker format, so a two +value policy (the pcr example above and authvalue) would look like +this in policymaker format:: + + 0000017F000000010004030000016768033e216468247bd031a0a2d9876d79818f8f + 0000016b + +This can be placed in a file (say policy.txt) and then added to the key as:: + + $ keyctl add trusted kmk "new 32 keyhandle=0x81000001 hash=sha1 policy=`cat policy.txt`" @u + +The newlines in the file policy.txt will be automatically processed. + Reseal a trusted key under new pcr values:: $ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`" diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c index 1c70f82ed170..6d69f0300584 100644 --- a/security/keys/trusted-keys/tpm2-policy.c +++ b/security/keys/trusted-keys/tpm2-policy.c @@ -338,3 +338,49 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols, } return 0; } + +int tpm2_parse_policies(struct tpm2_policies **ppols, char *str) +{ + struct tpm2_policies *pols; + char *p; + u8 *ptr; + int i = 0, left = PAGE_SIZE, res; + + pols = kmalloc(left, GFP_KERNEL); + if (!pols) + return -ENOMEM; + + ptr = (u8 *)(pols + 1); + left -= ptr - (u8 *)pols; + + while ((p = strsep(&str, "\n"))) { + if (*p == '\0' || *p == '\n') + continue; + pols->len[i] = strlen(p)/2; + if (pols->len[i] > left) { + res = -E2BIG; + goto err; + } + res = hex2bin(ptr, p, pols->len[i]); + if (res) + goto err; + /* get command code and skip past */ + pols->code[i] = get_unaligned_be32(ptr); + pols->policies[i] = ptr + 4; + ptr += pols->len[i]; + left -= pols->len[i]; + pols->len[i] -= 4; + /* + * FIXME: this does leave the code embedded in dead + * regions of the memory, but it's easier than + * hexdumping to a temporary or copying over + */ + i++; + } + pols->count = i; + *ppols = pols; + return 0; + err: + kfree(pols); + return res; +} diff --git a/security/keys/trusted-keys/tpm2-policy.h b/security/keys/trusted-keys/tpm2-policy.h index 152c948743f3..cb804a544ced 100644 --- a/security/keys/trusted-keys/tpm2-policy.h +++ b/security/keys/trusted-keys/tpm2-policy.h @@ -28,3 +28,4 @@ int tpm2_generate_policy_digest(struct tpm2_policies *pols, u32 hash, int tpm2_encode_policy(struct tpm2_policies *pols, u8 **data, u32 *len); int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols, u32 *handle); +int tpm2_parse_policies(struct tpm2_policies **ppols, char *str); diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 6290e611b632..ba05c75c3170 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -29,6 +29,8 @@ #include +#include "tpm2-policy.h" + static const char hmac_alg[] = "hmac(sha1)"; static const char hash_alg[] = "sha1"; static struct tpm_chip *chip; @@ -706,7 +708,7 @@ enum { Opt_new, Opt_load, Opt_update, Opt_keyhandle, Opt_keyauth, Opt_blobauth, Opt_pcrinfo, Opt_pcrlock, Opt_migratable, - Opt_hash, + Opt_hash, Opt_policy, }; static const match_table_t key_tokens = { @@ -720,6 +722,7 @@ static const match_table_t key_tokens = { {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, {Opt_hash, "hash=%s"}, + {Opt_policy, "policy=%s"}, {Opt_err, NULL} }; @@ -809,6 +812,15 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; } break; + case Opt_policy: + if (pay->policies) + return -EINVAL; + if (!tpm2) + return -EINVAL; + res = tpm2_parse_policies(&pay->policies, args[0].from); + if (res) + return res; + break; default: return -EINVAL; } From patchwork Tue Dec 10 00:10:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Bottomley X-Patchwork-Id: 11281099 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AA792138C for ; Tue, 10 Dec 2019 00:10:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 891DF2077B for ; Tue, 10 Dec 2019 00:10:20 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="FYR8i1yI"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=hansenpartnership.com header.i=@hansenpartnership.com header.b="FYR8i1yI" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727385AbfLJAKU (ORCPT ); Mon, 9 Dec 2019 19:10:20 -0500 Received: from bedivere.hansenpartnership.com ([66.63.167.143]:38224 "EHLO bedivere.hansenpartnership.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726495AbfLJAKT (ORCPT ); Mon, 9 Dec 2019 19:10:19 -0500 Received: from localhost (localhost [127.0.0.1]) by bedivere.hansenpartnership.com (Postfix) with ESMTP id 92E4B8EE112; Mon, 9 Dec 2019 16:10:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936619; bh=qXHPTGBNkTg68Sa4gU3uMyopIzQxxDozpkSYvTM99EM=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=FYR8i1yI5F9Xbx+98/FbtAEYKP4XMjsGj0K3bl138O/kTYJwnDmNO3qnJpGx/kVuX Xoh5bysO6E73/MWMSW9l2Pkb87X3Rc7054iTGvwhw6vhCcitbxfBgNXu9+2J0iCJtm 7JTA4bYjqK5svcDNmX4yCQSPjHEfhRt8s5HmK/Do= Received: from bedivere.hansenpartnership.com ([127.0.0.1]) by localhost (bedivere.hansenpartnership.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1HxD2SFPLquR; Mon, 9 Dec 2019 16:10:19 -0800 (PST) Received: from jarvis.lan (unknown [50.35.76.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by bedivere.hansenpartnership.com (Postfix) with ESMTPSA id 2DE018EE0FC; Mon, 9 Dec 2019 16:10:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=hansenpartnership.com; s=20151216; t=1575936619; bh=qXHPTGBNkTg68Sa4gU3uMyopIzQxxDozpkSYvTM99EM=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=FYR8i1yI5F9Xbx+98/FbtAEYKP4XMjsGj0K3bl138O/kTYJwnDmNO3qnJpGx/kVuX Xoh5bysO6E73/MWMSW9l2Pkb87X3Rc7054iTGvwhw6vhCcitbxfBgNXu9+2J0iCJtm 7JTA4bYjqK5svcDNmX4yCQSPjHEfhRt8s5HmK/Do= Message-ID: <1575936618.31378.58.camel@HansenPartnership.com> Subject: [PATCH v2 8/8] security: keys: trusted: implement counter/timer policy From: James Bottomley To: linux-integrity@vger.kernel.org Cc: Mimi Zohar , Jarkko Sakkinen , David Woodhouse , keyrings@vger.kernel.org Date: Mon, 09 Dec 2019 16:10:18 -0800 In-Reply-To: <1575936272.31378.50.camel@HansenPartnership.com> References: <1575936272.31378.50.camel@HansenPartnership.com> X-Mailer: Evolution 3.26.6 Mime-Version: 1.0 Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org This is actually a generic policy allowing a range of comparisons against any value set in the TPM Clock, which includes things like the reset count, a monotonic millisecond count and the restart count. The most useful comparison is against the millisecond count for expiring keys. However, you have to remember that currently Linux doesn't try to sync the epoch timer with the TPM, so the expiration is actually measured in how long the TPM itself has been powered on ... the TPM timer doesn't count while the system is powered down. The millisecond counter is a u64 quantity found at offset 8 in the timer structure, and the <= comparision operand is 9, so a policy set to expire after the TPM has been up for 100 seconds would look like 0000016d00000000000f424000080009 Where 0x16d is the counter timer policy code and 0xf4240 is 100 000 in hex. Signed-off-by: James Bottomley --- Documentation/security/keys/trusted-encrypted.rst | 29 +++++++++++++++++++++++ security/keys/trusted-keys/tpm2-policy.c | 19 +++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index ade1a9dc8367..52d8bd8bef65 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -235,3 +235,32 @@ about the usage can be found in the file Another new format 'enc32' has been defined in order to support encrypted keys with payload size of 32 bytes. This will initially be used for nvdimm security but may expand to other usages that require 32 bytes payload. + +Appendix +-------- + +TPM 2.0 Policies +---------------- + +The current TPM supports PCR lock policies as documented above and +CounterTimer policies which can be used to create expiring keys. One +caveat with expiring keys is that the TPM millisecond counter does not +update while a system is powered off and Linux does not sync the TPM +millisecond count with its internal clock, so the best you can expire +in is in terms of how long any given TPM has been powered on. (FIXME: +Linux should simply update the millisecond clock to the current number +of seconds past the epoch on boot). + +A CounterTimer policy is expressed in terms of length and offset +against the TPM clock structure (TPMS_TIME_INFO), which looks like the +packed structure:: + + struct tpms_time_info { + u64 uptime; /* time in ms since last start or reset */ + u64 clock; /* cumulative uptime in ms */ + u32 resetcount; /* numer of times the TPM has been reset */ + u32 restartcount; /* number of times the TPM has been restarted */ + u8 safe /* time was safely loaded from NVRam */ + }; + +The usual comparison for expiring keys is against clock, at offset 8. diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c index 6d69f0300584..f19beb3e9e49 100644 --- a/security/keys/trusted-keys/tpm2-policy.c +++ b/security/keys/trusted-keys/tpm2-policy.c @@ -322,6 +322,25 @@ int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols, tpm_buf_append(&buf, pols->policies[i], pols->len[i] - pols->hash_size); break; + case TPM2_CC_POLICY_COUNTER_TIMER: { + /* + * the format of this is the last two u16 + * quantities are the offset and operation + * respectively. The rest is operandB which + * must be zero padded in a hash digest + */ + u16 opb_len = pols->len[i] - 4; + + if (opb_len > pols->hash_size) + return -EINVAL; + + tpm_buf_append_u16(&buf, opb_len); + tpm_buf_append(&buf, pols->policies[i], opb_len); + /* offset and operand*/ + tpm_buf_append(&buf, pols->policies[i] + opb_len, 4); + failure = "Counter Timer"; + break; + } default: failure = "unknown policy"; break;