Re: [PATCH v9 1/4] KEYS: trusted: Add generic trusted keys framework

From: Sumit Garg
Date: Wed Apr 21 2021 - 07:08:40 EST


Hi James,

On Wed, 21 Apr 2021 at 04:47, James Bottomley <jejb@xxxxxxxxxxxxx> wrote:
>
> On Mon, 2021-03-01 at 18:41 +0530, Sumit Garg wrote:
> > Current trusted keys framework is tightly coupled to use TPM device
> > as an underlying implementation which makes it difficult for
> > implementations like Trusted Execution Environment (TEE) etc. to
> > provide trusted keys support in case platform doesn't posses a TPM
> > device.
> >
> > Add a generic trusted keys framework where underlying implementations
> > can be easily plugged in. Create struct trusted_key_ops to achieve
> > this, which contains necessary functions of a backend.
> >
> > Also, define a module parameter in order to select a particular trust
> > source in case a platform support multiple trust sources. In case its
> > not specified then implementation itetrates through trust sources
> > list starting with TPM and assign the first trust source as a backend
> > which has initiazed successfully during iteration.
> >
> > Note that current implementation only supports a single trust source
> > at runtime which is either selectable at compile time or during boot
> > via aforementioned module parameter.
>
> You never actually tested this, did you? I'm now getting EINVAL from
> all the trusted TPM key operations because of this patch.
>

Unfortunately, I don't possess a development machine with a TPM
device. So mine testing was entirely based on TEE as a backend which
doesn't support any optional parameters. And that being the reason I
didn't catch this issue at first instance.

Is there any TPM emulation environment available that I can use for testing?

> The reason is quite simple: this function:
>
> > index 000000000000..0db86b44605d
> > --- /dev/null
> > +++ b/security/keys/trusted-keys/trusted_core.c
> [...]
> > +static int datablob_parse(char *datablob, struct trusted_key_payload
> > *p)
> > +{
> > + substring_t args[MAX_OPT_ARGS];
> > + long keylen;
> > + int ret = -EINVAL;
> > + int key_cmd;
> > + char *c;
> > +
> > + /* main command */
> > + c = strsep(&datablob, " \t");
>
> Modifies its argument to consume tokens and separates them with NULL.
>
> so the arguments for
>
> keyctl add trusted kmk "new 34 keyhandle=0x81000001"
>
> Go into this function as
>
> datablob="new 34 keyhandle=0x81000001"
>
> After we leave it, it looks like
>
> datablob="new\034\0keyhandle=0x81000001"
>
> However here:
>
> > +static int trusted_instantiate(struct key *key,
> > + struct key_preparsed_payload *prep)
> > +{
> > + struct trusted_key_payload *payload = NULL;
> > + size_t datalen = prep->datalen;
> > + char *datablob;
> > + int ret = 0;
> > + int key_cmd;
> > + size_t key_len;
> > +
> > + if (datalen <= 0 || datalen > 32767 || !prep->data)
> > + return -EINVAL;
> > +
> > + datablob = kmalloc(datalen + 1, GFP_KERNEL);
> > + if (!datablob)
> > + return -ENOMEM;
> > + memcpy(datablob, prep->data, datalen);
> > + datablob[datalen] = '\0';
> > +
> > + payload = trusted_payload_alloc(key);
> > + if (!payload) {
> > + ret = -ENOMEM;
> > + goto out;
> > + }
> > +
> > + key_cmd = datablob_parse(datablob, payload);
> > + if (key_cmd < 0) {
> > + ret = key_cmd;
> > + goto out;
> > + }
> > +
> > + dump_payload(payload);
> > +
> > + switch (key_cmd) {
> > + case Opt_load:
> > + ret = static_call(trusted_key_unseal)(payload,
> > datablob);
>
> We're passing the unmodified
>
> datablob="new\034\0keyhandle=0x81000001"
>
> Into the tpm trusted_key_unseal function. However, it only sees "new"
> and promply gives EINVAL because you've removed the ability to process
> the new option from it. What should have happened is you should have
> moved data blob up to passed the consumed tokens, so it actually reads
>
> datablob="keyhandle=0x81000001"
>
> However, to do that you'd have to have the updated pointer passed out
> of your datablob_parse() above.

Thanks for the detailed explanation.

>
> There's also a lost !tpm2 in the check for options->keyhandle, but I
> suspect Jarkko lost that merging the two patches. I think what's below
> fixes all of this, so if you can test it for trusted_tee, I'll package
> it up as two separate patches fixing all of this.
>

Below fixes look good to me and I have tested them using TEE as a
backend too. So feel free to add:

Tested-by: Sumit Garg <sumit.garg@xxxxxxxxxx>

-Sumit

> James
>
> ---
>
> diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
> index ec3a066a4b42..7c636212429b 100644
> --- a/security/keys/trusted-keys/trusted_core.c
> +++ b/security/keys/trusted-keys/trusted_core.c
> @@ -62,7 +62,7 @@ static const match_table_t key_tokens = {
> *
> * On success returns 0, otherwise -EINVAL.
> */
> -static int datablob_parse(char *datablob, struct trusted_key_payload *p)
> +static int datablob_parse(char **datablob, struct trusted_key_payload *p)
> {
> substring_t args[MAX_OPT_ARGS];
> long keylen;
> @@ -71,14 +71,14 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p)
> char *c;
>
> /* main command */
> - c = strsep(&datablob, " \t");
> + c = strsep(datablob, " \t");
> if (!c)
> return -EINVAL;
> key_cmd = match_token(c, key_tokens, args);
> switch (key_cmd) {
> case Opt_new:
> /* first argument is key size */
> - c = strsep(&datablob, " \t");
> + c = strsep(datablob, " \t");
> if (!c)
> return -EINVAL;
> ret = kstrtol(c, 10, &keylen);
> @@ -89,7 +89,7 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p)
> break;
> case Opt_load:
> /* first argument is sealed blob */
> - c = strsep(&datablob, " \t");
> + c = strsep(datablob, " \t");
> if (!c)
> return -EINVAL;
> p->blob_len = strlen(c) / 2;
> @@ -138,7 +138,7 @@ static int trusted_instantiate(struct key *key,
> {
> struct trusted_key_payload *payload = NULL;
> size_t datalen = prep->datalen;
> - char *datablob;
> + char *datablob, *orig_datablob;
> int ret = 0;
> int key_cmd;
> size_t key_len;
> @@ -146,7 +146,7 @@ static int trusted_instantiate(struct key *key,
> if (datalen <= 0 || datalen > 32767 || !prep->data)
> return -EINVAL;
>
> - datablob = kmalloc(datalen + 1, GFP_KERNEL);
> + orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
> if (!datablob)
> return -ENOMEM;
> memcpy(datablob, prep->data, datalen);
> @@ -158,7 +158,7 @@ static int trusted_instantiate(struct key *key,
> goto out;
> }
>
> - key_cmd = datablob_parse(datablob, payload);
> + key_cmd = datablob_parse(&datablob, payload);
> if (key_cmd < 0) {
> ret = key_cmd;
> goto out;
> @@ -194,7 +194,7 @@ static int trusted_instantiate(struct key *key,
> ret = -EINVAL;
> }
> out:
> - kfree_sensitive(datablob);
> + kfree_sensitive(orig_datablob);
> if (!ret)
> rcu_assign_keypointer(key, payload);
> else
> @@ -218,7 +218,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
> struct trusted_key_payload *p;
> struct trusted_key_payload *new_p;
> size_t datalen = prep->datalen;
> - char *datablob;
> + char *datablob, *orig_datablob;
> int ret = 0;
>
> if (key_is_negative(key))
> @@ -229,7 +229,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
> if (datalen <= 0 || datalen > 32767 || !prep->data)
> return -EINVAL;
>
> - datablob = kmalloc(datalen + 1, GFP_KERNEL);
> + orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
> if (!datablob)
> return -ENOMEM;
>
> @@ -241,7 +241,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
>
> memcpy(datablob, prep->data, datalen);
> datablob[datalen] = '\0';
> - ret = datablob_parse(datablob, new_p);
> + ret = datablob_parse(&datablob, new_p);
> if (ret != Opt_update) {
> ret = -EINVAL;
> kfree_sensitive(new_p);
> @@ -265,7 +265,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
> rcu_assign_keypointer(key, new_p);
> call_rcu(&p->rcu, trusted_rcu_free);
> out:
> - kfree_sensitive(datablob);
> + kfree_sensitive(orig_datablob);
> return ret;
> }
>
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index 4e5c50138f92..bc702ba0a596 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -747,6 +747,9 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>
> opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
>
> + if (!c)
> + return 0;
> +
> while ((p = strsep(&c, " \t"))) {
> if (*p == '\0' || *p == ' ' || *p == '\t')
> continue;
> @@ -944,7 +947,7 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
> goto out;
> dump_options(options);
>
> - if (!options->keyhandle) {
> + if (!options->keyhandle && !tpm2) {
> ret = -EINVAL;
> goto out;
> }
>
>