lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <b12dad90-c9ed-2331-7e96-78ca5c3994e8@axis.com>
Date:   Fri, 30 Jul 2021 16:24:01 +0200
From:   Borys Movchan <borysmn@...s.com>
To:     Jarkko Sakkinen <jarkko@...nel.org>,
        Borys Movchan <Borys.Movchan@...s.com>
CC:     Peter Huewe <peterhuewe@....de>, Jason Gunthorpe <jgg@...pe.ca>,
        kernel <kernel@...s.com>,
        "linux-integrity@...r.kernel.org" <linux-integrity@...r.kernel.org>,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v2] tpm: Add Upgrade/Reduced mode support for TPM2 modules

On 7/28/21 11:58 PM, Jarkko Sakkinen wrote:
 > On Wed, Jul 28, 2021 at 12:57:30PM +0200, Borys Movchan wrote:
 > > If something went wrong during the TPM firmware upgrade,
 > > like power failure or the firmware image file get corrupted,
 > > the TPM might end up in Upgrade or Failure mode upon the
 > > next start. The state is persistent between the TPM power
 > > cycle/restart.
 > >
 > > According to TPM specification:
 > >  * If the TPM is in Upgrade mode, it will answer with
 > >    TPM2_RC_UPGRADE to all commands except Field Upgrade
 > >    related ones.
 > >  * If the TPM is in Failure mode, it will allow performing
 > >    TPM initialization but will not provide any crypto
 > >    operations. Will happily respond to Field Upgrade calls.
 > >
 > > The fix adds the possibility to detect an active state of
 > > the TPM and gives the user-space a chance to finish the
 > > firmware upgrade/recover the TPM.
 >
 > This is different than telling what the patch does. It's just
 > describing a goal, but does not describe how the driver is
 > changed, and reasons for doing that.
 >
 > For instance, you check 'limited_mode' flag in a few sites.
 > How can I know that those are exactly the locations where this
 > needs to be done?
 >

Seems like I got what you are looking for. Let me try to explain the 
reasoning
and doubts regarding what I meant under my change.

According to my previous comments the reason of the change is to provide 
a way
to user-space application to safely perform the TPM firmware update and 
cover
all corner cases like power failure, corrupted firmware image, bad
communication channel and so on.

In original implementation, if TPM is in Upgrade or Failure (some 
vendors call
it Reduced) mode the driver will simple fail during initialization and 
will not
provide any conventional way for the user-space application to address the
situation.

So the fix changes the behavior of `tpm_chip_start` function in a way 
that it
will try to distinguish between actual TPM start failure and  having TPM in
Upgrade/Failure mode.

The Upgrade mode is easy to distinguish. According to the TPM 
specification, in
this mode the TPM will answer to only 2 calls: 
`TPM_CC_FieldUpgradeStart` and
`TPM_CC_FieldUpgradeData`. It will respond `TPM_RC_UPGRADE` to any other 
call.

Failure mode, as mentioned earlier, a bit trickier, but possible to detect.
In this mode very limited functionality is supported as TPM runs some 
form of
fallback firmware or in fallback mode due to failure of upgrade. Different
vendors implement it slightly different. The way how the fix tries to 
address
this mode is by making sure that `TPM_CC_SelfTest` and `TPM_CC_Startup` are
executed correctly but all the consequent calls to address crypto 
functionality
of the TPM are failing. In other words, functions like 
`tpm2_get_cc_attrs_tbl`,
`tpm_add_hwrng` and `tpm_get_pcr_allocation` will fail.

Below you also can find more detailed comments inline regarding every 
part of
the change.

Important to note that the fix addresses only TPM2.0 compatible devices 
as that
is what I am working with.

 > > Signed-off-by: Borys Movchan <borysmn@...s.com>
 > > ---
 > >
 > > Notes:
 > >     v2: The terms are changed to match the ones used in the TPM 
specification.
 > >     Rework the commit message to provide more details regarding TPM
 > >     behavior in Failure/Upgrade mode.
 > >
 > >     The TPM specification describes TPM behavior in Upgrade mode 
very clearly.
 > >     Things are a bit more complex if we are talking about Failure mode.
 > >     The TPM behavior in this mode is highly vendor-specific. 
Although, there
 > >     is one thing clearly described in the TPM specification and can 
be relied
 > >     on to detect the Failure state: in Failure mode, the TPM 
doesn't provide
 > >     any crypto operations. Including access to attributes and 
configuration
 > >     registers.
 > >     It seems persistent between different TPM manufacturers, at 
least to the
 > >     degree I was able to verify.
 > >
 > >  drivers/char/tpm/tpm-chip.c | 23 +++++++++++++++--------
 > >  drivers/char/tpm/tpm2-cmd.c | 12 ++++++++++--
 > >  include/linux/tpm.h         |  1 +
 > >  3 files changed, 26 insertions(+), 10 deletions(-)
 > >
 > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
 > > index ddaeceb7e109..ff2367c447fb 100644
 > > --- a/drivers/char/tpm/tpm-chip.c
 > > +++ b/drivers/char/tpm/tpm-chip.c
 > > @@ -574,20 +574,25 @@ static int tpm_get_pcr_allocation(struct 
tpm_chip *chip)
 > >  int tpm_chip_register(struct tpm_chip *chip)
 > >  {
 > >        int rc;
 > > +     bool limited_mode = false;

Introduce this flat to mark that TPM is in Upgrade/Failure mode and the
functionality is limited.

 > >
 > >        rc = tpm_chip_start(chip);
 > >        if (rc)
 > >                return rc;
 > >        rc = tpm_auto_startup(chip);
 > > -     if (rc) {
 > > +     if (rc == -EIO) {
 > > +             limited_mode = true;
 > > +     } else if (rc) {

As described earlier, during startup we can distinguish what mode TPM is
running in.

So check the return value here and set limited_mode flag accordingly.

I am not sure that returning one of the errno values here is good choice
as it might correlate with legit error returned by hardware 
communication layer.
Any comments/suggestions are welcome.

 > >                tpm_chip_stop(chip);
 > >                return rc;
 > >        }
 > >
 > > -     rc = tpm_get_pcr_allocation(chip);
 > > -     tpm_chip_stop(chip);
 > > -     if (rc)
 > > -             return rc;
 > > +     if (!limited_mode) {
 > > +             rc = tpm_get_pcr_allocation(chip);
 > > +             tpm_chip_stop(chip);
 > > +             if (rc)
 > > +                     return rc;
 > > +     }

As mentioned above, if TPM is in Upgrade/Failure mode, the call to
tpm_get_pcr_allocation will 100% fail, so avoid calling it.

 > >
 > >        tpm_sysfs_add_device(chip);
 > >
 > > @@ -595,9 +600,11 @@ int tpm_chip_register(struct tpm_chip *chip)
 > >
 > >        tpm_add_ppi(chip);
 > >
 > > -     rc = tpm_add_hwrng(chip);
 > > -     if (rc)
 > > -             goto out_ppi;
 > > +     if (!limited_mode) {
 > > +             rc = tpm_add_hwrng(chip);
 > > +             if (rc)
 > > +                     goto out_ppi;
 > > +     }

Same as with tpm_get_pcr_allocation. Call to tpm_add_hwrng will fail when
limited_mode is set to true;

 > >
 > >        rc = tpm_add_char_device(chip);
 > >        if (rc)
 > > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
 > > index a25815a6f625..7468353ed67d 100644
 > > --- a/drivers/char/tpm/tpm2-cmd.c
 > > +++ b/drivers/char/tpm/tpm2-cmd.c
 > > @@ -718,7 +718,8 @@ static int tpm2_startup(struct tpm_chip *chip)
 > >   *                     sequence
 > >   * @chip: TPM chip to use
 > >   *
 > > - * Returns 0 on success, < 0 in case of fatal error.
 > > + * Returns 0 on success, -ENODEV in case of fatal error,
 > > + *       -EIO in case of Reduced/Upgrade mode
 > >   */
 > >  int tpm2_auto_startup(struct tpm_chip *chip)
 > >  {
 > > @@ -729,7 +730,10 @@ int tpm2_auto_startup(struct tpm_chip *chip)
 > >                goto out;
 > >
 > >        rc = tpm2_do_selftest(chip);
 > > -     if (rc && rc != TPM2_RC_INITIALIZE)
 > > +     if (rc == TPM2_RC_UPGRADE) {
 > > +             rc = -EIO;
 > > +             goto out;
 > > +     } else if (rc && rc != TPM2_RC_INITIALIZE)

Checking explicitly if TPM is in Upgrade mode. If it is, return immediately
as all other calls (except Firmware upgrade ones) to TPM will be failing.

 > >                goto out;
 > >
 > >        if (rc == TPM2_RC_INITIALIZE) {
 > > @@ -743,6 +747,10 @@ int tpm2_auto_startup(struct tpm_chip *chip)
 > >        }
 > >
 > >        rc = tpm2_get_cc_attrs_tbl(chip);
 > > +     if (rc) { /* Succeeded until here, but failed -> reduced mode */
 > > +             rc = -EIO;
 > > +             goto out;
 > > +     }

If execution context reaches this point but fails here, the conclusion 
can be
drawn that that TPM is in Failure mode and needs recovery. Indicate it by
settings appropriate return value and exit.

 > >
 > >  out:
 > >        if (rc > 0)
 > > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
 > > index aa11fe323c56..e873c42907f0 100644
 > > --- a/include/linux/tpm.h
 > > +++ b/include/linux/tpm.h
 > > @@ -207,6 +207,7 @@ enum tpm2_return_codes {
 > >        TPM2_RC_INITIALIZE      = 0x0100, /* RC_VER1 */
 > >        TPM2_RC_FAILURE         = 0x0101,
 > >        TPM2_RC_DISABLED        = 0x0120,
 > > +     TPM2_RC_UPGRADE         = 0x012D,
 > >        TPM2_RC_COMMAND_CODE    = 0x0143,
 > >        TPM2_RC_TESTING         = 0x090A, /* RC_WARN */
 > >        TPM2_RC_REFERENCE_H0    = 0x0910,
 > > --
 > > 2.20.1
 > >
 > >

Hope that's give a bit more light to what is it all about. Let me know 
if you
have any other questions, I would be glad to help.

 >
 > /Jarkko

Kind regards,
Borys

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ