[v4,21/24] powerpc/pseries: Pass PLPKS password on kexec

Message ID 20230120074306.1326298-22-ajd@linux.ibm.com
State New
Headers
Series pSeries dynamic secure boot secvar interface + platform keyring loading |

Commit Message

Andrew Donnellan Jan. 20, 2023, 7:43 a.m. UTC
  From: Russell Currey <ruscur@russell.cc>

Before interacting with the PLPKS, we ask the hypervisor to generate a
password for the current boot, which is then required for most further
PLPKS operations.

If we kexec into a new kernel, the new kernel will try and fail to
generate a new password, as the password has already been set.

Pass the password through to the new kernel via the device tree, in
/chosen/plpks-pw. Check for the presence of this property before trying
to generate a new password - if it exists, use the existing password and
remove it from the device tree.

Signed-off-by: Russell Currey <ruscur@russell.cc>
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>

---

v3: New patch

v4: Fix compile when CONFIG_PSERIES_PLPKS=n (snowpatch)

    Fix error handling on fdt_path_offset() call (ruscur)
---
 arch/powerpc/kexec/file_load_64.c      | 18 ++++++++++++++++++
 arch/powerpc/platforms/pseries/plpks.c | 18 +++++++++++++++++-
 2 files changed, 35 insertions(+), 1 deletion(-)
  

Comments

Nicholas Piggin Jan. 24, 2023, 4:36 a.m. UTC | #1
On Fri Jan 20, 2023 at 5:43 PM AEST, Andrew Donnellan wrote:
> From: Russell Currey <ruscur@russell.cc>
>
> Before interacting with the PLPKS, we ask the hypervisor to generate a
> password for the current boot, which is then required for most further
> PLPKS operations.
>
> If we kexec into a new kernel, the new kernel will try and fail to
> generate a new password, as the password has already been set.
>
> Pass the password through to the new kernel via the device tree, in
> /chosen/plpks-pw. Check for the presence of this property before trying

In /chosen/ibm,plpks-pw

> to generate a new password - if it exists, use the existing password and
> remove it from the device tree.
>
> Signed-off-by: Russell Currey <ruscur@russell.cc>
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
>
> ---
>
> v3: New patch
>
> v4: Fix compile when CONFIG_PSERIES_PLPKS=n (snowpatch)
>
>     Fix error handling on fdt_path_offset() call (ruscur)
> ---
>  arch/powerpc/kexec/file_load_64.c      | 18 ++++++++++++++++++
>  arch/powerpc/platforms/pseries/plpks.c | 18 +++++++++++++++++-
>  2 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
> index af8854f9eae3..0c9130af60cc 100644
> --- a/arch/powerpc/kexec/file_load_64.c
> +++ b/arch/powerpc/kexec/file_load_64.c
> @@ -27,6 +27,7 @@
>  #include <asm/kexec_ranges.h>
>  #include <asm/crashdump-ppc64.h>
>  #include <asm/prom.h>
> +#include <asm/plpks.h>
>  
>  struct umem_info {
>  	u64 *buf;		/* data buffer for usable-memory property */
> @@ -1156,6 +1157,9 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
>  {
>  	struct crash_mem *umem = NULL, *rmem = NULL;
>  	int i, nr_ranges, ret;
> +#ifdef CONFIG_PSERIES_PLPKS
> +	int chosen_offset;
> +#endif

Could put this in plpks_is_available and avoid an ifdef.

>  
>  	/*
>  	 * Restrict memory usage for kdump kernel by setting up
> @@ -1230,6 +1234,20 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
>  		}
>  	}
>  
> +#ifdef CONFIG_PSERIES_PLPKS
> +	// If we have PLPKS active, we need to provide the password
> +	if (plpks_is_available()) {
> +		chosen_offset = fdt_path_offset(fdt, "/chosen");
> +		if (chosen_offset < 0) {
> +			pr_err("Can't find chosen node: %s\n",
> +			       fdt_strerror(chosen_offset));
> +			goto out;
> +		}
> +		ret = fdt_setprop(fdt, chosen_offset, "ibm,plpks-pw",
> +				  plpks_get_password(), plpks_get_passwordlen());
> +	}
> +#endif // CONFIG_PSERIES_PLPKS

I think if you define plpks_get_password and plpks_get_passwordlen as
BUILD_BUG_ON when PLPKS is not configured and plpks_is_available as
false, you could remove the ifdef entirely.

> +
>  out:
>  	kfree(rmem);
>  	kfree(umem);
> diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
> index b3c7410a4f13..0350f10e1755 100644
> --- a/arch/powerpc/platforms/pseries/plpks.c
> +++ b/arch/powerpc/platforms/pseries/plpks.c
> @@ -16,6 +16,7 @@
>  #include <linux/slab.h>
>  #include <linux/string.h>
>  #include <linux/types.h>
> +#include <linux/of.h>
>  #include <asm/hvcall.h>
>  #include <asm/machdep.h>
>  #include <asm/plpks.h>
> @@ -126,7 +127,22 @@ static int plpks_gen_password(void)
>  {
>  	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
>  	u8 *password, consumer = PLPKS_OS_OWNER;
> -	int rc;
> +	struct property *prop;
> +	int rc, len;
> +
> +	// Before we generate the password, we may have been booted by kexec and
> +	// provided with a previous password.  Check for that first.

So not really generating the password then. Should it be in a different
function the caller makes first?

> +	prop = of_find_property(of_chosen, "ibm,plpks-pw", &len);
> +	if (prop) {
> +		ospasswordlength = (u16)len;
> +		ospassword = kzalloc(ospasswordlength, GFP_KERNEL);
> +		if (!ospassword) {
> +			of_remove_property(of_chosen, prop);
> +			return -ENOMEM;
> +		}
> +		memcpy(ospassword, prop->value, len);
> +		return of_remove_property(of_chosen, prop);

Why do you remove the property afterward?

Thanks,
Nick
  
Andrew Donnellan Jan. 24, 2023, 4:40 a.m. UTC | #2
On Tue, 2023-01-24 at 14:36 +1000, Nicholas Piggin wrote:
> 
> > +       prop = of_find_property(of_chosen, "ibm,plpks-pw", &len);
> > +       if (prop) {
> > +               ospasswordlength = (u16)len;
> > +               ospassword = kzalloc(ospasswordlength, GFP_KERNEL);
> > +               if (!ospassword) {
> > +                       of_remove_property(of_chosen, prop);
> > +                       return -ENOMEM;
> > +               }
> > +               memcpy(ospassword, prop->value, len);
> > +               return of_remove_property(of_chosen, prop);
> 
> Why do you remove the property afterward?

Because otherwise the password will be sitting around in /proc/device-
tree for the world to go and read.
  
Michael Ellerman Jan. 25, 2023, 3:59 a.m. UTC | #3
Andrew Donnellan <ajd@linux.ibm.com> writes:
> On Tue, 2023-01-24 at 14:36 +1000, Nicholas Piggin wrote:
>> 
>> > +       prop = of_find_property(of_chosen, "ibm,plpks-pw", &len);
>> > +       if (prop) {
>> > +               ospasswordlength = (u16)len;
>> > +               ospassword = kzalloc(ospasswordlength, GFP_KERNEL);
>> > +               if (!ospassword) {
>> > +                       of_remove_property(of_chosen, prop);
>> > +                       return -ENOMEM;
>> > +               }
>> > +               memcpy(ospassword, prop->value, len);
>> > +               return of_remove_property(of_chosen, prop);
>> 
>> Why do you remove the property afterward?
>
> Because otherwise the password will be sitting around in /proc/device-
> tree for the world to go and read.

The above removes it from the unflattened tree, but it will still exist
in the flattened tree, which is readable by root in /sys/firmware/fdt.

I'm not sure if that's a major security problem, but it does seem risky.

To scrub it from the flat tree you'd need to have an early_init_dt style
routine that finds the password early, saves the value somewhere for the
plpks driver, and then zeroes out the value in the flat tree. See the
example of rng-seed in early_init_dt_scan_chosen().

cheers
  
Russell Currey Jan. 31, 2023, 2:43 a.m. UTC | #4
On Tue, 2023-01-24 at 14:36 +1000, Nicholas Piggin wrote:
> On Fri Jan 20, 2023 at 5:43 PM AEST, Andrew Donnellan wrote:
> > From: Russell Currey <ruscur@russell.cc>
> > 
> > Before interacting with the PLPKS, we ask the hypervisor to
> > generate a
> > password for the current boot, which is then required for most
> > further
> > PLPKS operations.
> > 
> > If we kexec into a new kernel, the new kernel will try and fail to
> > generate a new password, as the password has already been set.
> > 
> > Pass the password through to the new kernel via the device tree, in
> > /chosen/plpks-pw. Check for the presence of this property before
> > trying
> 
> In /chosen/ibm,plpks-pw

Good catch, thanks

> 
> > to generate a new password - if it exists, use the existing
> > password and
> > remove it from the device tree.
> > 
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> > Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> > 
> > ---
> > 
> > v3: New patch
> > 
> > v4: Fix compile when CONFIG_PSERIES_PLPKS=n (snowpatch)
> > 
> >     Fix error handling on fdt_path_offset() call (ruscur)
> > ---
> >  arch/powerpc/kexec/file_load_64.c      | 18 ++++++++++++++++++
> >  arch/powerpc/platforms/pseries/plpks.c | 18 +++++++++++++++++-
> >  2 files changed, 35 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/powerpc/kexec/file_load_64.c
> > b/arch/powerpc/kexec/file_load_64.c
> > index af8854f9eae3..0c9130af60cc 100644
> > --- a/arch/powerpc/kexec/file_load_64.c
> > +++ b/arch/powerpc/kexec/file_load_64.c
> > @@ -27,6 +27,7 @@
> >  #include <asm/kexec_ranges.h>
> >  #include <asm/crashdump-ppc64.h>
> >  #include <asm/prom.h>
> > +#include <asm/plpks.h>
> >  
> >  struct umem_info {
> >         u64 *buf;               /* data buffer for usable-memory
> > property */
> > @@ -1156,6 +1157,9 @@ int setup_new_fdt_ppc64(const struct kimage
> > *image, void *fdt,
> >  {
> >         struct crash_mem *umem = NULL, *rmem = NULL;
> >         int i, nr_ranges, ret;
> > +#ifdef CONFIG_PSERIES_PLPKS
> > +       int chosen_offset;
> > +#endif
> 
> Could put this in plpks_is_available and avoid an ifdef.

Yep, moving this out, though not into plpks_is_available().

> 
> >  
> >         /*
> >          * Restrict memory usage for kdump kernel by setting up
> > @@ -1230,6 +1234,20 @@ int setup_new_fdt_ppc64(const struct kimage
> > *image, void *fdt,
> >                 }
> >         }
> >  
> > +#ifdef CONFIG_PSERIES_PLPKS
> > +       // If we have PLPKS active, we need to provide the password
> > +       if (plpks_is_available()) {
> > +               chosen_offset = fdt_path_offset(fdt, "/chosen");
> > +               if (chosen_offset < 0) {
> > +                       pr_err("Can't find chosen node: %s\n",
> > +                              fdt_strerror(chosen_offset));
> > +                       goto out;
> > +               }
> > +               ret = fdt_setprop(fdt, chosen_offset, "ibm,plpks-
> > pw",
> > +                                 plpks_get_password(),
> > plpks_get_passwordlen());
> > +       }
> > +#endif // CONFIG_PSERIES_PLPKS
> 
> I think if you define plpks_get_password and plpks_get_passwordlen as
> BUILD_BUG_ON when PLPKS is not configured and plpks_is_available as
> false, you could remove the ifdef entirely.

I'm moving this into a helper in plpks.c since now there's FDT handling
in early boot in there.  We can drop plpks_get_password() entirely.

> 
> > +
> >  out:
> >         kfree(rmem);
> >         kfree(umem);
> > diff --git a/arch/powerpc/platforms/pseries/plpks.c
> > b/arch/powerpc/platforms/pseries/plpks.c
> > index b3c7410a4f13..0350f10e1755 100644
> > --- a/arch/powerpc/platforms/pseries/plpks.c
> > +++ b/arch/powerpc/platforms/pseries/plpks.c
> > @@ -16,6 +16,7 @@
> >  #include <linux/slab.h>
> >  #include <linux/string.h>
> >  #include <linux/types.h>
> > +#include <linux/of.h>
> >  #include <asm/hvcall.h>
> >  #include <asm/machdep.h>
> >  #include <asm/plpks.h>
> > @@ -126,7 +127,22 @@ static int plpks_gen_password(void)
> >  {
> >         unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> >         u8 *password, consumer = PLPKS_OS_OWNER;
> > -       int rc;
> > +       struct property *prop;
> > +       int rc, len;
> > +
> > +       // Before we generate the password, we may have been booted
> > by kexec and
> > +       // provided with a previous password.  Check for that
> > first.
> 
> So not really generating the password then. Should it be in a
> different
> function the caller makes first?

Yes this should have been separate, and now has to be anyway since
we're retrieving the password from the FDT in early boot.

> 
> > +       prop = of_find_property(of_chosen, "ibm,plpks-pw", &len);
> > +       if (prop) {
> > +               ospasswordlength = (u16)len;
> > +               ospassword = kzalloc(ospasswordlength, GFP_KERNEL);
> > +               if (!ospassword) {
> > +                       of_remove_property(of_chosen, prop);
> > +                       return -ENOMEM;
> > +               }
> > +               memcpy(ospassword, prop->value, len);
> > +               return of_remove_property(of_chosen, prop);
> 
> Why do you remove the property afterward?

As Andrew mentioned, so we don't have a password lingering in the
device tree, though it's not especially useful.  We're going to get it
and clear it from the FDT in early boot instead.

> 
> Thanks,
> Nick
  

Patch

diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index af8854f9eae3..0c9130af60cc 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -27,6 +27,7 @@ 
 #include <asm/kexec_ranges.h>
 #include <asm/crashdump-ppc64.h>
 #include <asm/prom.h>
+#include <asm/plpks.h>
 
 struct umem_info {
 	u64 *buf;		/* data buffer for usable-memory property */
@@ -1156,6 +1157,9 @@  int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
 {
 	struct crash_mem *umem = NULL, *rmem = NULL;
 	int i, nr_ranges, ret;
+#ifdef CONFIG_PSERIES_PLPKS
+	int chosen_offset;
+#endif
 
 	/*
 	 * Restrict memory usage for kdump kernel by setting up
@@ -1230,6 +1234,20 @@  int setup_new_fdt_ppc64(const struct kimage *image, void *fdt,
 		}
 	}
 
+#ifdef CONFIG_PSERIES_PLPKS
+	// If we have PLPKS active, we need to provide the password
+	if (plpks_is_available()) {
+		chosen_offset = fdt_path_offset(fdt, "/chosen");
+		if (chosen_offset < 0) {
+			pr_err("Can't find chosen node: %s\n",
+			       fdt_strerror(chosen_offset));
+			goto out;
+		}
+		ret = fdt_setprop(fdt, chosen_offset, "ibm,plpks-pw",
+				  plpks_get_password(), plpks_get_passwordlen());
+	}
+#endif // CONFIG_PSERIES_PLPKS
+
 out:
 	kfree(rmem);
 	kfree(umem);
diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index b3c7410a4f13..0350f10e1755 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -16,6 +16,7 @@ 
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/of.h>
 #include <asm/hvcall.h>
 #include <asm/machdep.h>
 #include <asm/plpks.h>
@@ -126,7 +127,22 @@  static int plpks_gen_password(void)
 {
 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
 	u8 *password, consumer = PLPKS_OS_OWNER;
-	int rc;
+	struct property *prop;
+	int rc, len;
+
+	// Before we generate the password, we may have been booted by kexec and
+	// provided with a previous password.  Check for that first.
+	prop = of_find_property(of_chosen, "ibm,plpks-pw", &len);
+	if (prop) {
+		ospasswordlength = (u16)len;
+		ospassword = kzalloc(ospasswordlength, GFP_KERNEL);
+		if (!ospassword) {
+			of_remove_property(of_chosen, prop);
+			return -ENOMEM;
+		}
+		memcpy(ospassword, prop->value, len);
+		return of_remove_property(of_chosen, prop);
+	}
 
 	// The password must not cross a page boundary, so we align to the next power of 2
 	password = kzalloc(roundup_pow_of_two(maxpwsize), GFP_KERNEL);