mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH RFC 00/17] Add security policy support
@ 2025-08-14 13:06 Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 01/17] kconfig: allow setting CONFIG_ from the outside Ahmad Fatoum
                   ` (16 more replies)
  0 siblings, 17 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox

Security policies are a mechanism for barebox to prevent, when so
desired, security relevant code from being executed.

Security policies are controlled via a second Kconfig menu structure
(called Sconfig) which collects security relevant options.

While the normal Kconfig menu structure is about feature support
enabled at compile time, a security policy determines whether a
feature is allowed or prohibited at runtime with an explicit focus
on security.

Except for a security policy's name, all security options are
boolean and control whether a built-in feature is allowed:

  config FASTBOOT_CMD_BASE
  	bool
  	prompt "Allow fastboot flash/erase commands"
  	depends on $(kconfig-enabled,FASTBOOT_BASE)
  	help
  	  This option enables the fastboot "flash" and "erase" commands.

The depends directive ensures the option is hidden when Fastboot support
isn't compiled in anyway. Otherwise, enabling the option should permit
normal operation as if the security policy support was disabled.

Disabling the option, will have the relevant functions return early,
often with a permission denied error.

Checking the state of a security config option is done with the
IS_ALLOWED macro. The macro evaluates to true if the option is
defined and enabled in the active security policy and false otherwise.

A partial manipulation of the active security policy is not desirable
as it makes security posture at runtime harder to reason about.

It's expected that boards will define a fixed set of policies,
e.g. devel, factory, lockdown and then consult eFuses or JSON web tokens
to determine which policy is to be applied.

Some precautions have been made to make sure the security policies have
been reviewed and changes to the security options do not go through
unnoticed during barebox updates: Automatic config updates are
prohibited, so if new options are not present or the other way round,
the build will just fail. The user is expected to run e.g.
make security_olddefconfig to explicitly sync the configuration and
commit the changes.

Ahmad Fatoum (16):
  kconfig: allow setting CONFIG_ from the outside
  scripts: include scripts/include for all host tools
  kbuild: implement loopable loop_cmd
  Add security policy support
  kbuild: allow security config use without source tree modification
  defaultenv: update PS1 according to security policy
  security: policy: support externally provided configs
  docs: security-policies: add documentation
  commands: go: add security config option
  console: ratp: add security config option
  bootm: support calling bootm_optional_signed_images at any time
  bootm: make unsigned image support runtime configurable
  ARM: configs: add virt32_secure_defconfig
  boards: qemu-virt: add security policies
  boards: qemu-virt: allow setting policy from command line
  test: py: add basic security policy test

Sascha Hauer (1):
  commands: implement sconfig command

 .gitignore                                    |   4 +
 Documentation/devel/devel.rst                 |   1 +
 Documentation/devel/security-policies.rst     |  89 +++
 Documentation/user/defaultenv-2.rst           |   2 +
 Documentation/user/security-policies.rst      | 110 ++++
 Documentation/user/user-manual.rst            |   1 +
 Makefile                                      |  81 ++-
 Sconfig                                       |   9 +
 arch/arm/configs/virt32_secure_defconfig      | 302 ++++++++++
 commands/Kconfig                              |  23 +
 commands/Makefile                             |   1 +
 commands/Sconfig                              |  12 +
 commands/go.c                                 |   4 +
 commands/sconfig.c                            | 219 +++++++
 common/Kconfig                                |   5 +
 common/Sconfig                                |  24 +
 common/boards/qemu-virt/Makefile              |   5 +-
 common/boards/qemu-virt/board.c               |  11 +
 common/boards/qemu-virt/commandline.c         |  74 +++
 common/boards/qemu-virt/commandline.h         |   9 +
 .../qemu-virt/qemu-virt-factory.sconfig       |  24 +
 .../qemu-virt/qemu-virt-lockdown.sconfig      |  24 +
 common/bootm.c                                |  58 +-
 common/console.c                              |   4 +-
 common/ratp/ratp.c                            |  17 +
 defaultenv/Makefile                           |   1 +
 .../bin/ps1-policy                            |  20 +
 .../init/ps1-policy                           |   1 +
 .../init/source-colors                        |   1 +
 defaultenv/defaultenv.c                       |   2 +
 include/security/config.h                     |  76 +++
 include/security/defs.h                       |  22 +
 include/security/policy.h                     |  54 ++
 scripts/Kbuild.include                        |  41 ++
 scripts/Makefile                              |   1 -
 scripts/Makefile.build                        |  18 +-
 scripts/Makefile.lib                          |  47 ++
 scripts/Makefile.policy                       |  43 ++
 scripts/Sconfig.include                       |   6 +
 scripts/basic/.gitignore                      |   1 +
 scripts/basic/Makefile                        |   4 +-
 scripts/basic/sconfigpost.c                   | 540 ++++++++++++++++++
 scripts/include/list.h                        |   7 +
 scripts/kconfig/Makefile                      |   3 +
 scripts/kconfig/list.h                        | 132 -----
 security/Kconfig                              |   2 +
 security/Kconfig.policy                       | 101 ++++
 security/Makefile                             |  38 ++
 security/Sconfig                              |  42 ++
 security/policy.c                             | 246 ++++++++
 security/qemu-virt-devel.sconfig              |  24 +
 security/qemu-virt-tamper.sconfig             |  24 +
 security/sconfig_names.c                      |  18 +
 test/arm/virt32_secure_defconfig.yaml         |  22 +
 test/py/test_policies.py                      |  49 ++
 55 files changed, 2543 insertions(+), 156 deletions(-)
 create mode 100644 Documentation/devel/security-policies.rst
 create mode 100644 Documentation/user/security-policies.rst
 create mode 100644 Sconfig
 create mode 100644 arch/arm/configs/virt32_secure_defconfig
 create mode 100644 commands/Sconfig
 create mode 100644 commands/sconfig.c
 create mode 100644 common/Sconfig
 create mode 100644 common/boards/qemu-virt/commandline.c
 create mode 100644 common/boards/qemu-virt/commandline.h
 create mode 100644 common/boards/qemu-virt/qemu-virt-factory.sconfig
 create mode 100644 common/boards/qemu-virt/qemu-virt-lockdown.sconfig
 create mode 100755 defaultenv/defaultenv-2-security-policy/bin/ps1-policy
 create mode 100644 defaultenv/defaultenv-2-security-policy/init/ps1-policy
 create mode 100644 defaultenv/defaultenv-2-security-policy/init/source-colors
 create mode 100644 include/security/config.h
 create mode 100644 include/security/defs.h
 create mode 100644 include/security/policy.h
 create mode 100644 scripts/Makefile.policy
 create mode 100644 scripts/Sconfig.include
 create mode 100644 scripts/basic/sconfigpost.c
 create mode 100644 scripts/include/list.h
 delete mode 100644 scripts/kconfig/list.h
 create mode 100644 security/Kconfig.policy
 create mode 100644 security/Sconfig
 create mode 100644 security/policy.c
 create mode 100644 security/qemu-virt-devel.sconfig
 create mode 100644 security/qemu-virt-tamper.sconfig
 create mode 100644 security/sconfig_names.c
 create mode 100644 test/arm/virt32_secure_defconfig.yaml
 create mode 100644 test/py/test_policies.py

-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 01/17] kconfig: allow setting CONFIG_ from the outside
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 02/17] scripts: include scripts/include for all host tools Ahmad Fatoum
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Regular config options apply build-wide. Security config options will
be enabled depending on the policy that's currently active.

To allow using an SCONFIG_ prefix for the latter, introduce
KCONFIG_CONFIG_ to affect this.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 scripts/kconfig/Makefile | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 5a215880b268..c4dbdf9abfc7 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -29,6 +29,9 @@ KCONFIG_DEFCONFIG_LIST += arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)
 
 # We need this, in case the user has it in its environment
 unexport CONFIG_
+ifdef KCONFIG_CONFIG_
+export CONFIG_=$(KCONFIG_CONFIG_)
+endif
 
 config-prog	:= conf
 menuconfig-prog	:= mconf
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 02/17] scripts: include scripts/include for all host tools
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 01/17] kconfig: allow setting CONFIG_ from the outside Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 03/17] kbuild: implement loopable loop_cmd Ahmad Fatoum
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We already have one implementation of list.h, so we do not need to
replicate it of Kconfig specially. Make use of scripts/include for all
host tools to align us with what Linux is doing.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Makefile               |   6 +-
 scripts/Makefile       |   1 -
 scripts/basic/Makefile |   2 -
 scripts/include/list.h |   7 +++
 scripts/kconfig/list.h | 132 -----------------------------------------
 5 files changed, 11 insertions(+), 137 deletions(-)
 create mode 100644 scripts/include/list.h
 delete mode 100644 scripts/kconfig/list.h

diff --git a/Makefile b/Makefile
index 836176d541c3..209999618e88 100644
--- a/Makefile
+++ b/Makefile
@@ -410,8 +410,10 @@ KBUILD_USERHOSTCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \
 KBUILD_USERCFLAGS  := $(KBUILD_USERHOSTCFLAGS) $(USERCFLAGS)
 KBUILD_USERLDFLAGS := $(USERLDFLAGS)
 
-KBUILD_HOSTCFLAGS   := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS)
-KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS)
+KBUILD_HOSTCFLAGS   := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) \
+		       $(HOSTCFLAGS) -I $(srctree)/scripts/include
+KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) \
+		       -I $(srctree)/scripts/include
 KBUILD_HOSTLDFLAGS  := $(HOST_LFS_LDFLAGS) $(HOSTLDFLAGS)
 KBUILD_HOSTLDLIBS   := $(HOST_LFS_LIBS) $(HOSTLDLIBS)
 
diff --git a/scripts/Makefile b/scripts/Makefile
index 85e081544553..7d6bc5a8ab97 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -29,7 +29,6 @@ hostprogs-always-$(CONFIG_RISCV)			+= prelink-riscv
 hostprogs-always-$(CONFIG_RK_IMAGE)			+= rkimage
 HOSTCFLAGS_rkimage.o = `$(PKG_CONFIG) --cflags openssl`
 HOSTLDLIBS_rkimage = `$(PKG_CONFIG) --libs openssl`
-KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
 HOSTCFLAGS_mxsimage.o = `$(PKG_CONFIG) --cflags openssl`
 HOSTLDLIBS_mxsimage  = `$(PKG_CONFIG) --libs openssl`
 HOSTCFLAGS_omap3-usb-loader.o = `$(PKG_CONFIG) --cflags libusb-1.0`
diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile
index e48754e29924..eeb6a38c5551 100644
--- a/scripts/basic/Makefile
+++ b/scripts/basic/Makefile
@@ -3,5 +3,3 @@
 # fixdep: used to generate dependency information during build process
 
 hostprogs-always-y	+= fixdep
-
-KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
diff --git a/scripts/include/list.h b/scripts/include/list.h
new file mode 100644
index 000000000000..ced30b635f95
--- /dev/null
+++ b/scripts/include/list.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef LIST_H
+#define LIST_H
+
+#include "linux/list.h"
+
+#endif
diff --git a/scripts/kconfig/list.h b/scripts/kconfig/list.h
deleted file mode 100644
index 45cb237ab7ef..000000000000
--- a/scripts/kconfig/list.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef LIST_H
-#define LIST_H
-
-/*
- * Copied from include/linux/...
- */
-
-#undef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
-/**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr:        the pointer to the member.
- * @type:       the type of the container struct this is embedded in.
- * @member:     the name of the member within the struct.
- *
- */
-#define container_of(ptr, type, member) ({                      \
-	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
-	(type *)( (char *)__mptr - offsetof(type,member) );})
-
-
-struct list_head {
-	struct list_head *next, *prev;
-};
-
-
-#define LIST_HEAD_INIT(name) { &(name), &(name) }
-
-#define LIST_HEAD(name) \
-	struct list_head name = LIST_HEAD_INIT(name)
-
-/**
- * list_entry - get the struct for this entry
- * @ptr:	the &struct list_head pointer.
- * @type:	the type of the struct this is embedded in.
- * @member:	the name of the list_head within the struct.
- */
-#define list_entry(ptr, type, member) \
-	container_of(ptr, type, member)
-
-/**
- * list_for_each_entry	-	iterate over list of given type
- * @pos:	the type * to use as a loop cursor.
- * @head:	the head for your list.
- * @member:	the name of the list_head within the struct.
- */
-#define list_for_each_entry(pos, head, member)				\
-	for (pos = list_entry((head)->next, typeof(*pos), member);	\
-	     &pos->member != (head); 	\
-	     pos = list_entry(pos->member.next, typeof(*pos), member))
-
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos:	the type * to use as a loop cursor.
- * @n:		another type * to use as temporary storage
- * @head:	the head for your list.
- * @member:	the name of the list_head within the struct.
- */
-#define list_for_each_entry_safe(pos, n, head, member)			\
-	for (pos = list_entry((head)->next, typeof(*pos), member),	\
-		n = list_entry(pos->member.next, typeof(*pos), member);	\
-	     &pos->member != (head);					\
-	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
-
-/**
- * list_empty - tests whether a list is empty
- * @head: the list to test.
- */
-static inline int list_empty(const struct list_head *head)
-{
-	return head->next == head;
-}
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add(struct list_head *_new,
-			      struct list_head *prev,
-			      struct list_head *next)
-{
-	next->prev = _new;
-	_new->next = next;
-	_new->prev = prev;
-	prev->next = _new;
-}
-
-/**
- * list_add_tail - add a new entry
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- */
-static inline void list_add_tail(struct list_head *_new, struct list_head *head)
-{
-	__list_add(_new, head->prev, head);
-}
-
-/*
- * Delete a list entry by making the prev/next entries
- * point to each other.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_del(struct list_head *prev, struct list_head *next)
-{
-	next->prev = prev;
-	prev->next = next;
-}
-
-#define LIST_POISON1  ((void *) 0x00100100)
-#define LIST_POISON2  ((void *) 0x00200200)
-/**
- * list_del - deletes entry from list.
- * @entry: the element to delete from the list.
- * Note: list_empty() on entry does not return true after this, the entry is
- * in an undefined state.
- */
-static inline void list_del(struct list_head *entry)
-{
-	__list_del(entry->prev, entry->next);
-	entry->next = (struct list_head*)LIST_POISON1;
-	entry->prev = (struct list_head*)LIST_POISON2;
-}
-#endif
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 03/17] kbuild: implement loopable loop_cmd
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 01/17] kconfig: allow setting CONFIG_ from the outside Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 02/17] scripts: include scripts/include for all host tools Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 04/17] Add security policy support Ahmad Fatoum
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The normal cmd can not be used in foreach, because it's prefixed by @.
Add an optional loopable variant as well as a noop_cmd that's suitable
for use in nesten $(call ...) invocations that would otherwise lead to
Make aborting due to perceived cycles in log_print.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 scripts/Kbuild.include | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index e905b5466371..a23d27cba315 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -158,6 +158,12 @@ delete-on-interrupt = \
 # print and execute commands
 cmd = @$(if $(cmd_$(1)),set -e; $($(quiet)log_print) $(delete-on-interrupt) $(cmd_$(1)),:)
 
+# The normal 'cmd' above is not *loopable*
+loop_cmd = $(if $(cmd_$(1)),set -e; $($(quiet)log_print) $(cmd_$(1)),:) || exit;
+
+# Like loop_cmd, but without printing
+noop_cmd = $(if $(cmd_$(1)),$(cmd_$(1)),:)
+
 ###
 # if_changed      - execute command if any prerequisite is newer than
 #                   target, or command line has changed
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 04/17] Add security policy support
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 03/17] kbuild: implement loopable loop_cmd Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 05/17] kbuild: allow security config use without source tree modification Ahmad Fatoum
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@barebox.org>

Security policies are a mechanism for barebox to prevent, when so
desired, security relevant code from being executed.

Security policies are controlled via a second Kconfig menu structure
(called Sconfig) which collects security relevant options.

While the normal Kconfig menu structure is about feature support
enabled at compile time, a security policy determines whether a
feature is allowed or prohibited at runtime with an explicit focus
on security.

Except for a security policy's name, all security options are
boolean and control whether a built-in feature is allowed:

  config FASTBOOT_CMD_BASE
  	bool
  	prompt "Allow fastboot flash/erase commands"
  	depends on $(kconfig-enabled,FASTBOOT_BASE)
  	help
  	  This option enables the fastboot "flash" and "erase" commands.

The depends directive ensures the option is hidden when Fastboot support
isn't compiled in anyway. Otherwise, enabling the option should permit
normal operation as if the security policy support was disabled.

Disabling the option, will have the relevant functions return early,
often with a permission denied error.

Checking the state of a security config option is done with the
IS_ALLOWED macro. The macro evaluates to true if the option is
defined and enabled in the active security policy and false otherwise.

A partial manipulation of the active security policy is not desirable
as it makes security posture at runtime harder to reason about.

It's expected that boards will define a fixed set of policies,
e.g. devel, factory, lockdown and then consult eFuses or JSON web tokens
to determine which policy is to be applied.

Some precautions have been made to make sure the security policies have
been reviewed and changes to the security options do not go through
unnoticed during barebox updates: Automatic config updates are
prohibited, so if new options are not present or the other way round,
the build will just fail. The user is expected to run e.g.
make security_olddefconfig to explicitly sync the configuration and
commit the changes.

Co-developed-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 .gitignore                  |   4 +
 Makefile                    |  71 ++++-
 Sconfig                     |   7 +
 include/security/config.h   |  76 +++++
 include/security/defs.h     |  22 ++
 include/security/policy.h   |  50 ++++
 scripts/Kbuild.include      |  35 +++
 scripts/Makefile.build      |  18 +-
 scripts/Makefile.lib        |  47 ++++
 scripts/Makefile.policy     |  39 +++
 scripts/Sconfig.include     |   6 +
 scripts/basic/.gitignore    |   1 +
 scripts/basic/Makefile      |   2 +
 scripts/basic/sconfigpost.c | 540 ++++++++++++++++++++++++++++++++++++
 security/Kconfig            |   2 +
 security/Kconfig.policy     |  86 ++++++
 security/Makefile           |   2 +
 security/Sconfig            |  42 +++
 security/policy.c           | 243 ++++++++++++++++
 security/sconfig_names.c    |  18 ++
 20 files changed, 1305 insertions(+), 6 deletions(-)
 create mode 100644 Sconfig
 create mode 100644 include/security/config.h
 create mode 100644 include/security/defs.h
 create mode 100644 include/security/policy.h
 create mode 100644 scripts/Makefile.policy
 create mode 100644 scripts/Sconfig.include
 create mode 100644 scripts/basic/sconfigpost.c
 create mode 100644 security/Kconfig.policy
 create mode 100644 security/Sconfig
 create mode 100644 security/policy.c
 create mode 100644 security/sconfig_names.c

diff --git a/.gitignore b/.gitignore
index c37188a9f315..98bb4dac8912 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,9 @@
 *.patch
 *.pyc
 *.s
+*.sconfig.c
+*.sconfig.old
+*.sconfig.tmp
 *.so
 *.so.dbg
 *.symtypes
@@ -37,6 +40,7 @@
 binary.0
 Module.symvers
 dtbs-list
+policy-list
 *.dtb
 *.dtb.*
 *.dtbo
diff --git a/Makefile b/Makefile
index 209999618e88..a2e5697b09fe 100644
--- a/Makefile
+++ b/Makefile
@@ -278,7 +278,7 @@ ifneq ($(KBUILD_EXTMOD),)
 endif
 
 ifeq ($(KBUILD_EXTMOD),)
-        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
+        ifneq ($(filter config %config,$(filter-out security_%config,$(MAKECMDGOALS))),)
 		config-build := 1
                 ifneq ($(words $(MAKECMDGOALS)),1)
 			mixed-build := 1
@@ -450,6 +450,7 @@ AWK		= awk
 GENKSYMS	= scripts/genksyms/genksyms
 DEPMOD		= /sbin/depmod
 KALLSYMS	= scripts/kallsyms
+SCONFIGPOST	= scripts/basic/sconfigpost
 PERL		= perl
 PYTHON3		= python3
 CHECK		= sparse
@@ -524,7 +525,7 @@ LDFLAGS_elf += $(LDFLAGS_common) --nmagic -s
 export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC CXX
 export CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL PYTHON3 UTS_MACHINE
 export LEX YACC PROFDATA COV GENHTML
-export HOSTCXX CHECK CHECKFLAGS MKIMAGE
+export HOSTCXX CHECK CHECKFLAGS MKIMAGE SCONFIGPOST
 export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ
 export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
 export KBUILD_USERCFLAGS KBUILD_USERLDFLAGS
@@ -601,6 +602,8 @@ ifdef config-build
 # *config targets only - make sure prerequisites are updated, and descend
 # in scripts/kconfig to make the *config target
 
+# KCONFIG_CONFIG_ORIG is only set for policy kconfig processing
+ifndef KCONFIG_CONFIG_ORIG
 include $(srctree)/scripts/Makefile.defconf
 
 # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
@@ -608,6 +611,7 @@ include $(srctree)/scripts/Makefile.defconf
 # used for 'make defconfig'
 include $(srctree)/arch/$(SRCARCH)/Makefile
 export KBUILD_DEFCONFIG CC_VERSION_TEXT
+endif
 
 config: outputmakefile scripts_basic FORCE
 	$(Q)$(MAKE) $(build)=scripts/kconfig KCONFIG_DEFCONFIG_LIST= $@
@@ -1171,6 +1175,60 @@ include/generated/version.h: FORCE
 include/generated/utsrelease.h: include/config/kernel.release FORCE
 	$(call filechk,utsrelease.h)
 
+# ---------------------------------------------------------------------------
+# Security policies
+
+ifdef CONFIG_SECURITY_POLICY
+
+.security_config: $(KCONFIG_CONFIG) FORCE
+	+$(call cmd,sconfig,allyesconfig,$@.tmp,$@)
+	$(Q)if [ ! -r $@ ] || ! cmp -s $@.tmp $@; then	\
+		mv -f $@.tmp $@;			\
+	else						\
+		rm -f $@.tmp;				\
+	fi
+
+targets	+= .security_config
+targets += include/generated/security_autoconf.h
+targets += include/generated/sconfig_names.h
+
+KPOLICY = $(shell find $(objtree)/ -name policy-list -exec cat {} \;)
+KPOLICY.tmp = $(addsuffix .tmp,$(KPOLICY))
+
+PHONY += collect-policies
+collect-policies: KBUILD_MODULES :=
+collect-policies: KBUILD_BUILTIN :=
+collect-policies: $(barebox-dirs) FORCE
+
+PHONY += security_listconfigs
+security_listconfigs: collect-policies FORCE
+	@echo policies:
+	@$(foreach p, $(KPOLICY), echo $p ;)
+
+PHONY += security_checkconfigs
+security_checkconfigs: collect-policies $(KPOLICY.tmp) FORCE
+	+$(Q)$(foreach p, $(KPOLICY), \
+		$(call loop_cmd,security_checkconfig,$p.tmp))
+
+security_%config: collect-policies $(KPOLICY.tmp) FORCE
+	+$(Q)$(foreach p, $(KPOLICY), $(call loop_cmd,sconfig, \
+		$(@:security_%=%),$p.tmp))
+	+$(Q)$(foreach p, $(KPOLICY), \
+		cp 2>/dev/null $p.tmp $(call resolve-srctree,$p) || true;)
+
+quiet_cmd_sconfigpost = SCONFPP $@
+      cmd_sconfigpost = $(SCONFIGPOST) $2 -D $(depfile) -o $@ $<
+
+include/generated/security_autoconf.h: .security_config scripts_basic FORCE
+	$(call if_changed_dep,sconfigpost,-e)
+
+include/generated/sconfig_names.h: .security_config scripts_basic FORCE
+	$(call if_changed_dep,sconfigpost,-s)
+
+archprepare: include/generated/security_autoconf.h include/generated/sconfig_names.h
+
+endif
+
 # ---------------------------------------------------------------------------
 # Devicetree files
 
@@ -1299,8 +1357,8 @@ CLEAN_FILES +=	scripts/bareboxenv-target scripts/kernel-install-target \
 
 # Directories & files removed with 'make mrproper'
 MRPROPER_DIRS  += include/config usr/include include/generated Documentation/commands
-MRPROPER_FILES += .config .config.old .version .old_version \
-                  include/config.h           \
+MRPROPER_FILES += .config .config.old .security_config .version .old_version \
+                  include/config.h *.sconfig.old          \
 		  Module.symvers tags TAGS cscope*
 
 # clean - Delete most, but leave enough to build external modules
@@ -1321,7 +1379,8 @@ clean: archclean $(clean-dirs)
 		\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
 		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
 		-o -name '*lex.c' -o -name '.tab.[ch]' \
-		-o -name 'dtbs-list' \
+		-o -name 'dtbs-list' -o -name 'policy-list' \
+		-o -name '*.sconfig.tmp' -o -name '*.sconfig.[co]' \
 		-o -name '*.symtypes' -o -name '*.bbenv.*' -o -name "*.bbenv" \) \
 		-type f -print | xargs rm -f
 
@@ -1485,6 +1544,8 @@ target-dir = $(dir $@)
 	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
 %.symtypes: %.c prepare scripts FORCE
 	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.sconfig.tmp: %.sconfig prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
 
 # Modules
 %/: prepare scripts FORCE
diff --git a/Sconfig b/Sconfig
new file mode 100644
index 000000000000..ee6ddf53bccb
--- /dev/null
+++ b/Sconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+mainmenu "Barebox/$(ARCH) Security Configuration"
+
+source "scripts/Sconfig.include"
+
+source "security/Sconfig"
diff --git a/include/security/config.h b/include/security/config.h
new file mode 100644
index 000000000000..b37ef4272c94
--- /dev/null
+++ b/include/security/config.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BAREBOX_SECURITY_CONFIG_H
+#define __BAREBOX_SECURITY_CONFIG_H
+
+/*
+ * Security policies is an access control mechanism to control when
+ * security-sensitive code is allowed to run.
+ *
+ * This header is included by security-sensitive code to consult
+ * the policy and enforce it.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <notifier.h>
+#include <security/defs.h>
+
+extern const char *sconfig_names[SCONFIG_NUM];
+
+int sconfig_lookup(const char *name);
+
+extern struct notifier_head sconfig_notifier_list;
+
+bool is_allowed(const struct security_policy *policy, unsigned option);
+
+#define IF_ALLOWABLE(opt, then, else) \
+	({ __if_defined(opt##_DEFINED, then, else); })
+
+#ifdef CONFIG_SECURITY_POLICY
+#define IS_ALLOWED(opt)		IF_ALLOWABLE(opt, is_allowed(NULL, (opt)), 0)
+#define ALLOWABLE_VALUE(opt)	IF_ALLOWABLE(opt, opt, -1)
+#else
+#define IS_ALLOWED(opt)		1
+#define ALLOWABLE_VALUE(opt)	(-1)
+#endif
+
+static inline int sconfig_register_handler(struct notifier_block *nb,
+					   int (*cb)(struct notifier_block *,
+						     unsigned long, void *))
+{
+	if (!IS_ENABLED(CONFIG_SECURITY_POLICY))
+		return -ENOSYS;
+
+	nb->notifier_call = cb;
+	return notifier_chain_register(&sconfig_notifier_list, nb);
+}
+
+static inline int sconfig_unregister_handler(struct notifier_block *nb)
+{
+	if (!IS_ENABLED(CONFIG_SECURITY_POLICY))
+		return -ENOSYS;
+	return notifier_chain_unregister(&sconfig_notifier_list, nb);
+}
+
+struct sconfig_notifier_block;
+typedef void (*sconfig_notifier_callback_t)(struct sconfig_notifier_block *,
+					    enum security_config_option,
+					    bool val);
+
+struct sconfig_notifier_block {
+	struct notifier_block nb;
+	enum security_config_option opt;
+	sconfig_notifier_callback_t cb_filtered;
+};
+
+int __sconfig_register_handler_filtered(struct sconfig_notifier_block *nb,
+					sconfig_notifier_callback_t cb,
+					enum security_config_option);
+
+#define sconfig_register_handler_filtered(nb, cb, opt) ({ \
+	int __sopt = ALLOWABLE_VALUE(opt); \
+	__sopt != -1	?  __sconfig_register_handler_filtered((nb), (cb), __sopt) \
+			: -ENOSYS; \
+})
+
+#endif /* __BAREBOX_SECURITY_CONFIG_H */
diff --git a/include/security/defs.h b/include/security/defs.h
new file mode 100644
index 000000000000..5b478039ad94
--- /dev/null
+++ b/include/security/defs.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BAREBOX_SECURITY_DEFS_H
+#define __BAREBOX_SECURITY_DEFS_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_SECURITY_POLICY
+# include <generated/security_autoconf.h>
+#else
+#define SCONFIG_NUM 0
+enum security_config_option { SCONFIG__DUMMY__ };
+#endif
+
+extern const char *sconfig_names[SCONFIG_NUM];
+
+struct security_policy {
+       const char *name;
+       bool chained;
+       unsigned char policy[SCONFIG_NUM];
+};
+
+#endif
diff --git a/include/security/policy.h b/include/security/policy.h
new file mode 100644
index 000000000000..6f270793bc80
--- /dev/null
+++ b/include/security/policy.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BAREBOX_SECURITY_POLICY_H
+#define __BAREBOX_SECURITY_POLICY_H
+
+/*
+ * Security policies is an access control mechanism to control when
+ * security-sensitive code is allowed to run.
+ *
+ * This header is included by board code that registers and
+ * selects (activates) these security policies.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <notifier.h>
+#include <security/defs.h>
+
+/*
+ * It's recommended to use the following names for the
+ * "standard" policies
+ */
+#define POLICY_DEVEL		"devel"
+#define POLICY_FACTORY		"factory"
+#define POLICY_LOCKDOWN		"lockdown"
+#define POLICY_TAMPER		"tamper"
+#define POLICY_FIELD_RETURN	"return"
+
+extern const struct security_policy *active_policy;
+
+const struct security_policy *security_policy_get(const char *name);
+
+int security_policy_activate(const struct security_policy *policy);
+int security_policy_select(const char *name);
+void security_policy_list(void);
+
+#ifdef CONFIG_SECURITY_POLICY
+int __security_policy_register(const struct security_policy policy[]);
+#else
+static inline int __security_policy_register(const struct security_policy policy[])
+{
+	return -ENOSYS;
+}
+#endif
+
+#define security_policy_add(name) ({				\
+	extern const struct security_policy __policy_##name[];	\
+	__security_policy_register(__policy_##name);		\
+})
+
+#endif /* __BAREBOX_SECURITY_POLICY_H */
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index a23d27cba315..b4c3f92af2ff 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -225,6 +225,14 @@ cmd_and_fixdep =                                                             \
 # and if so will execute $(rule_foo).
 if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)
 
+# Same as newer-prereqs, but allows to exclude specified extra dependencies
+newer_prereqs_except = $(filter-out $(PHONY) $(1),$?)
+
+# Same as if_changed, but allows to exclude specified extra dependencies
+if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),      \
+	$(cmd);                                                              \
+	printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
+
 ###
 # why - tell why a target got built
 #       enabled by make V=2
@@ -282,3 +290,30 @@ ifneq ($(and $(filter notintermediate, $(.FEATURES)),$(filter-out 4.4,$(MAKE_VER
 else
 .SECONDARY:
 endif
+
+###############################################################################
+## barebox specifics
+###############################################################################
+
+# File resolution
+# ===========================================================================
+
+# turn an absolute path relative if it's beneath the specified directory
+strip-path = $(patsubst $(2)/%,%,$(1))
+strip-srctree = $(call strip-path,$(1),$(srctree))
+strip-objtree = $(call strip-path,$(1),$(objtree))
+
+# turn a relative path absolute if it exists by prefixing it with the
+# specified directory
+resolve-path = $(strip $(if $(1), $(if $(filter /%,$(1)), $(1), \
+        $(if $(wildcard $(2)/$(1)), $(2)/$(1), $(1) ) ), $(1) ))
+resolve-srctree = $(call resolve-path,$(1),$(srctree))
+resolve-objtree = $(call resolve-path,$(1),$(objtree))
+
+# resolve a relative path first beneath srctree if it exists there
+# and otherwise beneath objtree
+resolve-external = $(call resolve-objtree,$(call resolve-srctree,$(1)))
+
+# Read file in filechk
+# ===========================================================================
+filechk_cat = cat $<
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 591da3d750ec..d6b4f0344a31 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -15,6 +15,7 @@ obj-m :=
 lib-y :=
 lib-m :=
 pbl-y :=
+policy-y :=
 always :=
 always-y :=
 always-m :=
@@ -60,6 +61,12 @@ ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)
 include $(srctree)/scripts/Makefile.dtbs
 endif
 
+ifdef CONFIG_SECURITY_POLICY
+ifneq ($(policy-y)$(policy-)$(filter %.sconfig %.sconfig.tmp %.sconfig.c %.sconfig.o,$(targets)),)
+include $(srctree)/scripts/Makefile.policy
+endif
+endif
+
 ifndef obj
 $(warning kbuild: Makefile.build is included improperly)
 endif
@@ -73,6 +80,13 @@ cmd_gen_order = { $(foreach m, $(real-prereqs), \
 	$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
 	> $@
 
+# This is a list of source files from the current Makefile and its
+# sub-directories. The timestamp should be updated when any of the member files.
+
+cmd_gen_order_src = { $(foreach m, $(patsubst $(srctree)/%,%,$(real-prereqs)), \
+	$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
+	> $@
+
 ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
 lib-target := $(obj)/lib.a
 endif
@@ -278,12 +292,14 @@ intermediate_targets = $(foreach sfx, $(2), \
 # %.dtb.pbl.o <- %.dtb.S <- %.dtb <- %.dts (Barebox only)
 # %.lex.o <- %.lex.c <- %.l
 # %.tab.o <- %.tab.[ch] <- %.y
+# %.sconfig.o <- %.sconfig.c <- %.sconfig.tmp <- %.sconfig
 targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
 	   $(call intermediate_targets, .dtb.o, .dtb.S .dtb.z .dtb) \
 	   $(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo.z .dtbo) \
 	   $(call intermediate_targets, .dtb.pbl.o, .dtb.S .dtb.z .dtb) \
 	   $(call intermediate_targets, .lex.o, .lex.c) \
-	   $(call intermediate_targets, .tab.o, .tab.c .tab.h)
+	   $(call intermediate_targets, .tab.o, .tab.c .tab.h) \
+	   $(call intermediate_targets, .sconfig.o, .sconfig.c .sconfig.tmp)
 
 # Descending
 # ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index e6f0e254960a..f84d1456526c 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -85,6 +85,13 @@ extra-y += $(patsubst %,%.bbenv$(DEFAULT_COMPRESSION_SUFFIX),$(bbenv-y))
 extra-y += $(patsubst %,%.bbenv$(DEFAULT_COMPRESSION_SUFFIX).S,$(bbenv-y))
 extra-y += $(patsubst %,%.bbenv$(DEFAULT_COMPRESSION_SUFFIX).o,$(bbenv-y))
 
+ifdef CONFIG_SECURITY_POLICY
+obj-y	+= $(addsuffix .o, $(policy-y))
+extra-	+= $(patsubst %,%.c,$(policy-))
+extra-m	+= $(patsubst %,%.c,$(policy-m))
+extra-y	+= $(patsubst %,%.c,$(policy-y))
+endif
+
 # Replace multi-part objects by their individual parts,
 # including built-in.a from subdirectories
 real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))
@@ -652,3 +659,43 @@ cmd_public_keys = \
 
 %: %.base64
 	$(call cmd,b64dec)
+
+# Security Policy Handling
+# ---------------------------------------------------------------------------
+
+# Process security Kconfig file
+# TODO: set KCONFIG_WARN_UNKNOWN_SYMBOLS & KCONFIG_WERROR once supported
+quiet_cmd_sconfig = SCONFIG $(notdir $(or $(4),$(3)))
+      cmd_sconfig = \
+           KBUILD_KCONFIG=Sconfig			\
+           KCONFIG_CONFIG_ORIG=$(KCONFIG_CONFIG)	\
+           KCONFIG_CONFIG=$(3)				\
+           KCONFIG_CONFIG_=SCONFIG_			\
+           KBUILD_DEFCONFIG=allnoconfig			\
+           KCONFIG_NOSILENTUPDATE=1			\
+           KCONFIG_AUTOCONFIG=/dev/null			\
+           KCONFIG_AUTOHEADER=/dev/null			\
+           $(MAKE) -f $(srctree)/Makefile $(2) quiet=silent_
+
+# Check that a policy is up to date
+# NOTE: With KCONFIG_NOSILENTUPDATE=1, a simpler implementation is
+# possible: $(cmd_sconfig) syncconfig $(2), but it has the downside
+# of a worse error message, so we don't do that here
+quiet_cmd_security_checkconfig = SCONFIG $(notdir $(2))
+      cmd_security_checkconfig =				\
+	trap "rm -f $(2).tmp $(2).tmp.old" EXIT;		\
+	cp $(2) $(2).tmp ;					\
+        $(call noop_cmd,sconfig,olddefconfig,$(2).tmp,$(2)) ;	\
+	if ! cmp -s $(2).tmp $(2); then				\
+		echo >&2 '***'; \
+		echo >&2 '*** Security policy' \
+		         $(notdir $(2)) \
+		         'was not up to date.'; \
+		echo >&2 '***'; \
+		echo >&2 '*** Please run "make security_olddefconfig" or'; \
+		echo >&2 '*** another configurator and commit the results to'; \
+		echo >&2 '*** '$(2); \
+		echo >&2 '***'; \
+		$(if $(Q),,diff -u $(2) $(2).tmp >&2;) \
+		exit 1; \
+	fi ; rm -f $(2).tmp $(2).tmp.old
diff --git a/scripts/Makefile.policy b/scripts/Makefile.policy
new file mode 100644
index 000000000000..4c71774bbbc9
--- /dev/null
+++ b/scripts/Makefile.policy
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+real-policy-y   := $(addprefix $(obj)/, $(policy-y))
+
+targets         += $(addsuffix .tmp, $(real-policy-y))
+
+# policy-list
+# ---------------------------------------------------------------------------
+
+subdir-policylist := $(addsuffix /policy-list, $(subdir-ym))
+real-policy-y   += $(subdir-policylist)
+
+ifneq ($(policy-y)$(policy-),)
+always-y        += $(obj)/policy-list
+
+$(subdir-policylist): $(obj)/%/policy-list: $(obj)/% ;
+
+$(obj)/policy-list: $(real-policy-y) FORCE
+	$(call if_changed,gen_order_src)
+endif
+
+# sconfigpost (.sconfig -> .sconfig.c -> .sconfig.o)
+# ---------------------------------------------------------------------------
+
+$(obj)/%.sconfig.tmp: $(src)/%.sconfig FORCE
+	$(call filechk,cat)
+
+quiet_cmd_sconfigpost_c = SCONFPP $@
+      cmd_sconfigpost_c = $(SCONFIGPOST) -o $@ -D$(depfile) $(2)
+
+$(obj)/%.sconfig.c: quiet_cmd_sconfig :=
+$(obj)/%.sconfig.c: $(obj)/%.sconfig.tmp FORCE
+	+$(Q)$(call noop_cmd,security_checkconfig,$<)
+	$(call if_changed_dep,sconfigpost_c,$<)
+
+# targets
+# ---------------------------------------------------------------------------
+
+targets += $(always-y)
diff --git a/scripts/Sconfig.include b/scripts/Sconfig.include
new file mode 100644
index 000000000000..3f5e4c652d97
--- /dev/null
+++ b/scripts/Sconfig.include
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "scripts/Kconfig.include"
+
+kconfig-state = $(shell,CONFIG_=CONFIG_ $(srctree)/scripts/config --file "$(KCONFIG_CONFIG_ORIG)" -s "$(1)")
+kconfig-enabled = $(shell,echo $(kconfig-state,$(1)))
diff --git a/scripts/basic/.gitignore b/scripts/basic/.gitignore
index 961c91c8a884..660035252b31 100644
--- a/scripts/basic/.gitignore
+++ b/scripts/basic/.gitignore
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 /fixdep
+/sconfigpost
diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile
index eeb6a38c5551..cd4d9f321d63 100644
--- a/scripts/basic/Makefile
+++ b/scripts/basic/Makefile
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 # fixdep: used to generate dependency information during build process
+# sconfigpost: used to postprocess security configs
 
 hostprogs-always-y	+= fixdep
+hostprogs-always-y	+= sconfigpost
diff --git a/scripts/basic/sconfigpost.c b/scripts/basic/sconfigpost.c
new file mode 100644
index 000000000000..171eb8f43e58
--- /dev/null
+++ b/scripts/basic/sconfigpost.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Postprocess Sconfig files
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <utime.h>
+
+static void usage(int exitcode);
+
+static const char *argv0;
+static char *symbol_name = "untitled";
+static const char *policy_name = "untitled";
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+enum print { PRINT_ARRAY, PRINT_ENUM, PRINT_DEFINES, PRINT_STRINGS };
+
+#define panic(fmt, ...) do {                                    \
+	fprintf(stderr, "%s: " fmt, argv0, ##__VA_ARGS__);      \
+	exit(6);                                                \
+} while (0)
+
+#define xasprintf(args...) ({           \
+	char *_buf;                     \
+	if (asprintf(&_buf, args) < 0)  \
+	panic("asprintf: %m\n");        \
+	_buf;                           \
+})
+
+#define xstrdup(args...) nonnull(strdup(args))
+#define xmalloc(args...) nonnull(malloc(args))
+#define xrealloc(args...) nonnull(realloc(args))
+
+#ifdef DEBUG
+#define debug(args...) fprintf(stderr, args)
+#else
+#define debug(args...) (void)0
+#endif
+
+static inline size_t str_has_prefix(const char *str, const char *prefix)
+{
+	size_t len = strlen(prefix);
+	return strncmp(str, prefix, len) == 0 ? len : 0;
+}
+
+static void *nonnull(void *ptr)
+{
+	if (!ptr)
+		exit(2);
+	return ptr;
+}
+
+static FILE *xfopen(const char *path, const char *mode)
+{
+	FILE *fp;
+
+	fp = fopen(path, mode);
+	if (!fp)
+		panic("failed to open \"%s\" with mode '%s': %m\n",
+		      path, mode);
+
+	return fp;
+}
+
+static void print_global_header(FILE *out, enum print print)
+{
+	switch (print) {
+	case PRINT_ARRAY:
+		fprintf(out, "#include <security/defs.h>\n");
+		fprintf(out, "const struct security_policy __policy_%s[] = {\n", symbol_name);
+		break;
+	case PRINT_STRINGS:
+		fprintf(out, "#include <security/defs.h>\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static void print_header(FILE *out, enum print print, bool is_last)
+{
+	switch (print) {
+	case PRINT_ARRAY:
+		fprintf(out, "{\n");
+		fprintf(out, "\t.name = \"%s\",\n", policy_name);
+		fprintf(out, "\t.chained = %d,\n", is_last ? 0 : 1);
+		fprintf(out, "\t.policy = {\n");
+		break;
+	case PRINT_ENUM:
+		fprintf(out, "enum security_config_option {\n");
+		break;
+	case PRINT_STRINGS:
+		fprintf(out, "const char *sconfig_names[SCONFIG_NUM] = {\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static void print_elem(FILE *out, enum print print,
+		       const char *config, bool enable, int i)
+{
+	switch (print) {
+	case PRINT_DEFINES:
+		fprintf(out, "#define %-40s %u\n", config, i);
+		break;
+	case PRINT_ARRAY:
+		fprintf(out, "\t[%-40s] = %s,\n", config,
+			enable ? "1" : "0");
+		break;
+	case PRINT_ENUM:
+		fprintf(out, "\t%s = %i,\n", config, i);
+		break;
+	case PRINT_STRINGS:
+		fprintf(out, "\t[%s] = \"%s\",\n", config, config);
+		break;
+	}
+}
+
+static void print_footer(FILE *out, enum print print, bool is_last, int i)
+{
+	switch (print) {
+	case PRINT_DEFINES:
+		return;
+	case PRINT_ENUM:
+		fprintf(out, "\tSCONFIG_NUM = %i\n};\n", i);
+		break;
+	case PRINT_ARRAY:
+		if (is_last)
+			fprintf(out, "\t}}\n");
+		else
+			fprintf(out, "\t}},\n");
+		break;
+	case PRINT_STRINGS:
+		fprintf(out, "};\n");
+		break;
+	}
+}
+
+static void print_global_footer(FILE *out, enum print print)
+{
+	switch (print) {
+	case PRINT_ARRAY:
+		fprintf(out, "};\n");
+		break;
+	default:
+		break;
+	}
+}
+
+static const char *nextline(char **line, FILE *in)
+{
+	size_t len = 0;
+	ssize_t nread;
+
+	do {
+		nread = getline(line, &len, in);
+		if (nread < 0)
+			return NULL;
+	} while (!nread);
+
+	if (nread > 256)
+		panic("line \"%s\" exceeds maximum length of 256\n", *line);
+
+	if ((*line)[nread - 1] == '\n')
+		(*line)[nread - 1] = '\0';
+
+	return *line;
+}
+
+#define CONFIG_VAL_SKIP		(void *)1
+
+static const char *parse_config_val(char *line, bool *enable)
+{
+	static char config[256];
+	static char name[256];
+	char end[2];
+	int val_offset;
+	int ret;
+
+	if ((ret = sscanf(line, "# %255[A-Za-z0-9_] is not se%1[t]",
+			  config, end)) == 2) {
+		if (str_has_prefix(config, "SCONFIG_POLICY_"))
+			return CONFIG_VAL_SKIP;
+		if (enable)
+			*enable = false;
+	} else if ((ret = sscanf(line, "SCONFIG_POLICY_%255[A-Za-z0-9_]=%255s",
+				 config, name)) == 2) {
+		if (!strcmp(config, "NAME")) {
+			policy_name = name + 1;
+			*strchrnul(policy_name, '"') = '\0';
+		}
+		return CONFIG_VAL_SKIP;
+	} else if ((ret = sscanf(line, "%255[A-Za-z0-9_]=%n", config,
+				 &val_offset)) == 1) {
+		if (strcmp(&line[val_offset], "y"))
+			panic("\"%s\": bool data type expected\n", line);
+
+		if (enable)
+			*enable = true;
+	} else {
+		return NULL;
+	}
+
+	return config;
+}
+
+static void strsanitize(char *str)
+{
+	size_t i;
+
+	while ((i = strcspn(str, "-."))) {
+		switch (str[i]) {
+		case '-':
+			str[i] = '_';
+			break;
+		case '.':
+			str[i] = '\0';
+			/* fallthrough */
+		case '\0':
+			return;
+		}
+
+		str += i + 1;
+	}
+}
+
+static int parse_ext(const struct dirent *dir)
+{
+	const char *ext;
+
+	if (!dir || dir->d_type == DT_DIR)
+		return 0;
+
+	ext = strrchr(dir->d_name, '.');
+	if (!ext || ext == dir->d_name)
+		return 0;
+
+	return strcmp(ext, ".sconfig") == 0;
+}
+
+static time_t newest_mtime;
+
+static void stat_inputfile(const char *path, struct stat *st)
+{
+	if (stat(path, st))
+		panic("Input file '%s' doesn't exist: %m\n", path);
+
+	if (!S_ISDIR(st->st_mode))
+		newest_mtime = MAX(newest_mtime, st->st_mtime);
+}
+
+static char **collect_input(int argc, char *argv[])
+{
+	char **buf;
+	int i = 0;
+
+	argc++;
+	buf = xmalloc(argc * sizeof(*buf));
+
+	for (char **arg = argv; *arg; arg++) {
+		char **newbuf;
+		struct stat st;
+		struct dirent **namelist;
+		int n;
+
+		stat_inputfile(*arg, &st);
+
+		if (!S_ISDIR(st.st_mode)) {
+			buf[i++] = *arg;
+			continue;
+		}
+
+		n = scandir(*arg, &namelist, parse_ext, alphasort);
+		if (n < 0)
+			panic("scandir: %m\n");
+
+		argc += n;
+
+		newbuf = xrealloc(buf, argc * sizeof(*newbuf));
+		buf = newbuf;
+
+		for (int j = 0; j < n; j++) {
+			buf[i + j] = xasprintf("%s/%s", *arg, namelist[j]->d_name);
+			free(namelist[j]);
+
+			/* update newest_times */
+			stat_inputfile(buf[i + j], &st);
+		}
+
+		free(namelist);
+		i += n;
+	}
+
+	buf[i] = NULL;
+	return buf;
+}
+
+static long fsize(FILE *fp)
+{
+	long size;
+
+	fseek(fp, 0, SEEK_END);
+
+	size = ftell(fp);
+	if (size < 0)
+		panic("ftell: %m\n");
+
+	fseek(fp, 0, SEEK_SET);
+
+	return size;
+}
+
+static bool fidentical(FILE *fp1, FILE *fp2)
+{
+	int ch1, ch2;
+	if (fsize(fp1) != fsize(fp2)) {
+		debug("file size mismatch\n");
+		return false;
+	}
+
+	do {
+		ch1 = getc(fp1);
+		ch2 = getc(fp2);
+		if (ch1 != ch2)
+			return false;
+	} while (ch1 != EOF && ch2 != EOF);
+
+	return true;
+}
+
+static char *make_tmp_path(const char *path, FILE **fp)
+{
+
+	const char *filename, *slash;
+	char *tmpfilepath;
+
+	slash = strrchr(path, '/');
+	filename = slash ? slash + 1 : path;
+
+	if (slash)
+		tmpfilepath = xasprintf("%.*s/.%s.tmp", (int)(slash - path), path,
+					filename);
+	else
+		tmpfilepath = xasprintf(".%s.tmp", filename);
+
+	*fp = xfopen(tmpfilepath, "w+");
+
+	return tmpfilepath;
+}
+
+static void append_dependency(FILE *depfile, const char *path)
+{
+	char *abspath;
+
+	if (!depfile)
+		return;
+
+	if (!path) {
+		fprintf(depfile, "\n");
+		return;
+	}
+
+	abspath = nonnull(realpath(path, NULL));
+
+	fprintf(depfile, "\t%s \\\n", abspath);
+
+	free(abspath);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *outfilepath = NULL;
+	char *tmpfilepath = NULL;
+	char **infilepaths;
+	enum print print = PRINT_ARRAY;
+	FILE *in = stdin, *out = stdout, *out_final = NULL, *depfile = NULL;
+	char *line = NULL;
+	int opt;
+
+	argv0 = argv[0];
+
+	while ((opt = getopt(argc, argv, "esdD:o:h")) > 0) {
+		switch (opt) {
+		case 'e':
+			print = PRINT_ENUM;
+			break;
+		case 's':
+			print = PRINT_STRINGS;
+			break;
+		case 'd':
+			print = PRINT_DEFINES;
+			break;
+		case 'D':
+			depfile = xfopen(optarg, "w");
+			break;
+		case 'o':
+			outfilepath = optarg;
+			break;
+		case 'h':
+			usage(0);
+			break;
+		default:
+			usage(1);
+			break;
+		}
+	}
+
+	if (depfile && !outfilepath)
+		panic("can't generate depfile without -o argument\n");
+
+	if (argc - optind > 1 && print != PRINT_ARRAY)
+		panic("processing multiple files at once only possible for array mode\n");
+
+	if (argc == optind && depfile)
+		panic("can't generate depfile while reading stdin\n");
+
+	if (outfilepath) {
+		out_final = fopen(outfilepath, "r+");
+		if (out_final) {
+			tmpfilepath = make_tmp_path(outfilepath, &out);
+		} else if (outfilepath) {
+			out = xfopen(outfilepath, "w");
+		}
+
+		symbol_name = xasprintf("%s", basename(outfilepath));
+		strsanitize(symbol_name);
+	}
+
+	infilepaths = collect_input(argc - optind, &argv[optind]);
+
+	print_global_header(out, print);
+
+	if (depfile)
+		fprintf(depfile, "%s: \\\n", outfilepath);
+
+	for (char **infilepath = infilepaths; *infilepath; infilepath++) {
+		bool is_last = infilepath[1] == NULL;
+		bool hdr_printed = false;
+		const char *comment_prefix = "";
+		int option_idx = 0;
+
+		in = xfopen(*infilepath, "r");
+
+		while (nextline(&line, in)) {
+			const char *config;
+			bool enable;
+
+			config = parse_config_val(line, &enable);
+			if (config == CONFIG_VAL_SKIP)
+				continue;
+			if (!config) {
+				if (line[0] == '#')
+					fprintf(out, "%s// %s\n", comment_prefix, line + 1);
+
+				continue;
+			}
+
+			if (!hdr_printed) {
+				print_header(out, print, is_last);
+				if (print != PRINT_DEFINES)
+					comment_prefix = "\t";
+				hdr_printed = true;
+			}
+
+			print_elem(out, print, config, enable, option_idx++);
+		}
+
+		print_footer(out, print, is_last, option_idx);
+		fputs("\n", out);
+
+		if (print != PRINT_ARRAY) {
+			rewind(in);
+
+			while (nextline(&line, in)) {
+				const char *config = parse_config_val(line, NULL);
+				if (config && config != CONFIG_VAL_SKIP)
+					fprintf(out, "#define\t%s_DEFINED 1\n", config);
+			}
+		}
+
+		free(line);
+		fclose(in);
+
+		append_dependency(depfile, *infilepath);
+	}
+
+	print_global_footer(out, print);
+
+	fflush(out);
+
+	if (out_final) {
+		if (fidentical(out, out_final)) {
+			struct stat st;
+
+			debug("removing %s\n", tmpfilepath);
+			remove(tmpfilepath);
+
+			if (stat(outfilepath, &st))
+				panic("Output file '%s' doesn't exist?? %m\n", outfilepath);
+			if (st.st_mtime <= newest_mtime)
+				utime(outfilepath, NULL);
+		} else {
+			debug("renaming %s to %s\n", tmpfilepath, outfilepath);
+			rename(tmpfilepath, outfilepath);
+		}
+	}
+
+	append_dependency(depfile, "include/generated/autoconf.h");
+	if (print == PRINT_ARRAY || print == PRINT_STRINGS)
+		append_dependency(depfile, "include/generated/security_autoconf.h");
+	append_dependency(depfile, NULL);
+
+	return 0;
+}
+
+static void usage(int exitcode)
+{
+	FILE *fp = exitcode ? stderr : stdout;
+
+	fprintf(fp,
+		"usage: %s [-o FILE] [-d DEPFILE] [-edph] [config]\n"
+		"This script postprocess a barebox Sconfig (Security config)\n"
+		"in the Kconfig .config format for further use inside barebox.\n"
+		"If no positional argument is specified, the script operates on stdin\n"
+		"Options:\n"
+		"  -e             Output policy as C enumeration\n"
+		"  -s             Output policy option names as C array of strings\n"
+		"  -d             Output policy as C preprocessor defines\n"
+		"  -D <depfile>   Write depedencies into <depfile>\n"
+		"  -o <outfile>   Write output into <outfile> instead of stdout\n"
+		"  -h		  This help text\n", argv0);
+
+	exit(exitcode);
+}
diff --git a/security/Kconfig b/security/Kconfig
index 372fd275fde9..de58002f9656 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -17,6 +17,8 @@ config INSECURE
 
 	    - changes the default of global.env.autoprobe to 1
 
+source "security/Kconfig.policy"
+
 config PASSWORD
 	bool
 	prompt "Password Framework"
diff --git a/security/Kconfig.policy b/security/Kconfig.policy
new file mode 100644
index 000000000000..6c5cb5687c17
--- /dev/null
+++ b/security/Kconfig.policy
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# barebox Security Policy Support
+
+menuconfig SECURITY_POLICY
+	bool "Security Policy Support"
+	help
+	  A Security policy is a collection of security configuration options
+	  that can be activated together at runtime.
+	  Together, the describe a security state that barebox should operate in.
+
+	  Policies can be registered by board code or supplied by
+	  an external build system. The policy name must be unique across
+	  all registered policies.
+
+	  Policies are selected by name and only one can be active at a given time.
+	  barebox does not mandate any specific behavior for a policy according
+	  to its name. Boards have full freedom to name policies and configure the
+	  options as they deem appropriate.
+
+	  However, we recommend using established terms to make it easier to reason
+	  about the different security states:
+
+	    devel     Security policy should permit everything for
+	              development purposes.
+
+	    factory   System is in a secure boot mode, but policy allows
+	              interactive use for factory bring up purposes.
+	              Board code usually enforces via eFuse that factory
+	              mode can not be re-selected once deselected.
+
+	    lockdown  Factory bring up is done and device is ready for use
+	              in the field with barebox as part of the secure boot
+	              chain. This policy usually disallows booting unsigned
+	              images
+
+	    tamper    Tampering attempt was detected. The security policy would
+	              take steps to protect secrets (up to bricking the device).
+
+	    return    For use in field-return devices, the policy should
+	              take steps to unlock the device for analysis purposes.
+	              Board code should make sure to delete secret and
+	              confidential data before activating this policy.
+
+if SECURITY_POLICY
+
+config SECURITY_POLICY_INIT
+	string
+	prompt "Initial security policy"
+	help
+	  The policy named here will be automatically selected the first
+	  time a security policy is to be consulted.
+	  It's recommended to use a restrictive policy here and remove
+	  the restrictions if needed instead of the other way round.
+
+choice
+	prompt "Initial Security Policy"
+	default SECURITY_POLICY_DEFAULT_PERMISSIVE
+
+config SECURITY_POLICY_DEFAULT_PERMISSIVE
+	bool "Permissive by default"
+	select HAS_INSECURE_DEFAULTS
+	help
+	  In absence of a selected security policy, everything is allowed.
+	  A warning will be printed the first time a security policy would
+	  need to be consulted.
+
+	  This is a development aid and unsuitable for use in the field:
+	  A security policy should always be selected, either early on by
+	  board code or via CONFIG_SECURITY_POLICY_INIT.
+
+config SECURITY_POLICY_DEFAULT_PANIC
+	bool "Panic"
+	help
+	  In absence of a selected security policy, panic on the first
+	  time a security policy is to be consulted.
+
+	  If CONFIG_SECURITY_POLICY_INIT is not set, this expects a
+	  security policy to be selected prior to late_initcall.
+
+endchoice
+
+config SECURITY_POLICY_NAMES
+	bool
+
+endif
diff --git a/security/Makefile b/security/Makefile
index de9778620d28..16b328266a1b 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
+obj-$(CONFIG_SECURITY_POLICY)		+= policy.o
+obj-$(CONFIG_SECURITY_POLICY_NAMES)	+= sconfig_names.o
 obj-$(CONFIG_CRYPTO_KEYSTORE)	+= keystore.o
 obj-$(CONFIG_JWT)		+= jwt.o
 obj-pbl-$(CONFIG_HAVE_OPTEE)	+= optee.o
diff --git a/security/Sconfig b/security/Sconfig
new file mode 100644
index 000000000000..f7bf67e6e05b
--- /dev/null
+++ b/security/Sconfig
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Note: Symbols starting with POLICY_ are reserved and handled specially
+config POLICY_NAME
+        string "Policy name"
+	help
+	  Policies are selected by name and only one can be active at a given time.
+	  barebox does not mandate any specific behavior for a policy according
+	  to its name. Boards have full freedom to name policies and configure the
+	  options as they deem appropriate.
+
+	  However, we recommend using established terms to make it easier to reason
+	  about the different security states:
+
+	    devel     Security policy should permit everything for
+	              development purposes.
+
+	    factory   System is in a secure boot mode, but policy allows
+	              interactive use for factory bring up purposes.
+	              Board code usually enforces via eFuse that factory
+	              mode can not be re-selected once deselected.
+
+	    lockdown  Factory bring up is done and device is ready for use
+	              in the field with barebox as part of the secure boot
+	              chain. This policy usually disallows booting unsigned
+	              images
+
+	    tamper    Tampering attempt was detected. The security policy would
+	              take steps to protect secrets (up to bricking the device).
+
+	    return    For use in field-return devices, the policy should
+	              take steps to unlock the device for analysis purposes.
+	              Board code should make sure to delete secret and
+	              confidential data before activating this policy.
+
+config SECURITY_POLICY_SELECT
+	bool "Allow selecting a different security policy"
+	depends on $(kconfig-enabled,SECURITY_POLICY)
+	default y
+	help
+	  Say n here if selecting this policy is final and all subsequent calls
+	  to security_policy_select should be refused.
diff --git a/security/policy.c b/security/policy.c
new file mode 100644
index 000000000000..10d6148866ab
--- /dev/null
+++ b/security/policy.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "policy: " fmt
+
+#include <init.h>
+#include <linux/printk.h>
+#include <linux/bitmap.h>
+#include <param.h>
+#include <device.h>
+#include <stdio.h>
+
+#include <security/policy.h>
+#include <security/config.h>
+
+static const char *sconfig_name(unsigned option)
+{
+	static char name[sizeof("4294967295")];
+
+	if (IS_ENABLED(CONFIG_SECURITY_POLICY_NAMES))
+		return sconfig_names[option];
+
+	snprintf(name, sizeof(name), "%u", option);
+	return name;
+}
+
+#define policy_debug(policy, opt, fmt, ...) \
+	pr_debug("(%s) %s: " fmt, \
+		 (policy) ? (policy)->name : "default", \
+		 sconfig_name(opt), ##__VA_ARGS__)
+
+#ifdef CONFIG_SECURITY_POLICY_DEFAULT_PERMISSIVE
+#define policy_err			pr_err
+#define policy_warn_once		pr_warn_once
+#define policy_WARN			WARN
+#else
+#define policy_err(fmt, ...)		panic(pr_fmt(fmt), ##__VA_ARGS__)
+#define policy_warn_once(fmt, ...)	panic(pr_fmt(fmt), ##__VA_ARGS__)
+#define policy_WARN			BUG
+#endif
+
+struct policy_list_entry {
+	const struct security_policy *policy;
+	struct list_head list;
+};
+
+const struct security_policy *active_policy;
+
+static LIST_HEAD(policy_list);
+NOTIFIER_HEAD(sconfig_notifier_list);
+
+static bool __is_allowed(const struct security_policy *policy, unsigned option)
+{
+	if (!policy)
+		return true;
+
+	return policy->policy[option];
+}
+
+bool is_allowed(const struct security_policy *policy, unsigned option)
+{
+	policy = policy ?: active_policy;
+
+	if (WARN(option > SCONFIG_NUM))
+		return false;
+
+	if (!policy && *CONFIG_SECURITY_POLICY_INIT) {
+		security_policy_select(CONFIG_SECURITY_POLICY_INIT);
+		policy = active_policy;
+	}
+
+	if (policy) {
+		bool allow = __is_allowed(policy, option);
+
+		policy_debug(policy, option, "%s for %pS\n",
+			 allow ? "allowed" : "denied", (void *)_RET_IP_);
+
+		return allow;
+	}
+
+	if (IS_ENABLED(CONFIG_SECURITY_POLICY_DEFAULT_PERMISSIVE))
+		pr_warn_once("option %s checked before security policy was set!\n",
+			     sconfig_name(option));
+	else
+		panic(pr_fmt("option %s checked before security policy was set!"),
+		      sconfig_name(option));
+
+	return true;
+}
+
+int security_policy_activate(const struct security_policy *policy)
+{
+	const struct security_policy *old_policy = active_policy;
+
+	if (policy == old_policy)
+		return 0;
+
+	if (old_policy && !IS_ALLOWED(SCONFIG_SECURITY_POLICY_SELECT)) {
+		pr_err("Policy %s is permanent once selected\n",
+		       active_policy->name);
+		return -EPERM;
+	}
+
+	active_policy = policy;
+
+	for (int i = 0; i < SCONFIG_NUM; i++) {
+		if (__is_allowed(policy, i) == __is_allowed(old_policy, i))
+			continue;
+
+		notifier_call_chain(&sconfig_notifier_list, i, NULL);
+	}
+
+	return 0;
+}
+
+const struct security_policy *security_policy_get(const char *name)
+{
+	const struct policy_list_entry *entry;
+
+	list_for_each_entry(entry, &policy_list, list) {
+		if (!strcmp(name, entry->policy->name))
+			return entry->policy;
+	}
+
+	return NULL;
+}
+
+int security_policy_select(const char *name)
+{
+	const struct security_policy *policy;
+
+	policy = security_policy_get(name);
+	if (!policy) {
+		policy_err("Policy '%s' not found!\n", name);
+		return -ENOENT;
+	}
+
+	return security_policy_activate(policy);
+}
+
+int __security_policy_register(const struct security_policy policy[])
+{
+	int ret = 0;
+
+	do {
+		struct policy_list_entry *entry;
+
+		if (security_policy_get(policy->name)) {
+			policy_err("policy '%s' already registered\n", policy->name);
+			ret = -EBUSY;
+			continue;
+		}
+
+		entry = xzalloc(sizeof(*entry));
+		entry->policy = policy;
+		list_add_tail(&entry->list, &policy_list);
+	} while ((policy++)->chained);
+
+	return ret;
+}
+
+#ifdef CONFIG_CMD_SCONFIG
+void security_policy_unregister_one(const struct security_policy *policy)
+{
+	struct policy_list_entry *entry;
+
+	if (!policy)
+		return;
+
+	list_for_each_entry(entry, &policy_list, list) {
+		if (entry->policy == policy) {
+			list_del(&entry->list);
+			return;
+		}
+	}
+}
+#endif
+
+void security_policy_list(void)
+{
+	const struct policy_list_entry *entry;
+
+	list_for_each_entry(entry, &policy_list, list) {
+		printf("%s\n", entry->policy->name);
+	}
+}
+
+static int sconfig_handler_filtered(struct notifier_block *nb,
+				    unsigned long opt, void *data)
+{
+	struct sconfig_notifier_block *snb
+		= container_of(nb, struct sconfig_notifier_block, nb);
+	bool allow;
+
+	if (snb->opt != opt)
+		return NOTIFY_DONE;
+
+	allow = is_allowed(NULL, opt);
+
+	policy_debug(active_policy, opt, "calling %pS to %s\n",
+		     snb->cb_filtered, allow ? "allow" : "deny");
+
+	snb->cb_filtered(snb, opt, is_allowed(NULL, opt));
+	return NOTIFY_OK;
+}
+
+int __sconfig_register_handler_filtered(struct sconfig_notifier_block *snb,
+					sconfig_notifier_callback_t cb,
+					enum security_config_option opt)
+{
+	snb->cb_filtered = cb;
+	snb->opt = opt;
+	return sconfig_register_handler(&snb->nb, sconfig_handler_filtered);
+}
+
+struct device security_device = {
+	.name = "security",
+	.id = DEVICE_ID_SINGLE,
+};
+
+static char *policy_name = "";
+
+static int security_policy_get_name(struct param_d *param, void *priv)
+{
+	if (!active_policy) {
+		policy_name = "";
+		return 0;
+	}
+
+	free_const(policy_name);
+	policy_name = strdup(active_policy->name);
+	return 0;
+}
+
+static int security_init(void)
+{
+	register_device(&security_device);
+
+	dev_add_param_string(&security_device, "policy", param_set_readonly,
+			     security_policy_get_name, &policy_name, NULL);
+
+	return 0;
+}
+pure_initcall(security_init);
diff --git a/security/sconfig_names.c b/security/sconfig_names.c
new file mode 100644
index 000000000000..c830c4eb3892
--- /dev/null
+++ b/security/sconfig_names.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <security/config.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/kernel.h>
+
+#include <generated/sconfig_names.h>
+
+int sconfig_lookup(const char *name)
+{
+	for (int i = 0; i < ARRAY_SIZE(sconfig_names); i++) {
+		if (!strcmp(name, sconfig_names[i]))
+			return i;
+	}
+
+	return -ENOENT;
+}
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 05/17] kbuild: allow security config use without source tree modification
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 04/17] Add security policy support Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 06/17] defaultenv: update PS1 according to security policy Ahmad Fatoum
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

A key aspect of security policies is the enforcement of a policy to be
complete with no implicit defaults. To make this easier to use, the
security_*config targets directly manipulate the specified KPOLICY or
all known policies if none were specified.

This is at odds with build systems that assume an immutable source tree
and prefer that changes to files within purview of the build system are
only done explicitly by the user. For that purpose, add an optional
KPOLICY_TMPUPDATE, which works as follows:

  - When set, only the tmp file in the build tree is updated, but not the
    original
  - The tmp file is always what's used in the build
  - Once unset, the tmp file will always be overwritten by the original
    on next build

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Makefile                | 4 +++-
 scripts/Makefile.policy | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index a2e5697b09fe..6027b5c37c82 100644
--- a/Makefile
+++ b/Makefile
@@ -100,7 +100,7 @@ ifeq ($(silence),s)
 quiet=silent_
 endif
 
-export quiet Q KBUILD_VERBOSE
+export quiet Q KBUILD_VERBOSE KPOLICY_TMPUPDATE
 
 # Kbuild will save output files in the current working directory.
 # This does not need to match to the root of the kernel source tree.
@@ -1213,8 +1213,10 @@ security_checkconfigs: collect-policies $(KPOLICY.tmp) FORCE
 security_%config: collect-policies $(KPOLICY.tmp) FORCE
 	+$(Q)$(foreach p, $(KPOLICY), $(call loop_cmd,sconfig, \
 		$(@:security_%=%),$p.tmp))
+ifeq ($(KPOLICY_TMPUPDATE),)
 	+$(Q)$(foreach p, $(KPOLICY), \
 		cp 2>/dev/null $p.tmp $(call resolve-srctree,$p) || true;)
+endif
 
 quiet_cmd_sconfigpost = SCONFPP $@
       cmd_sconfigpost = $(SCONFIGPOST) $2 -D $(depfile) -o $@ $<
diff --git a/scripts/Makefile.policy b/scripts/Makefile.policy
index 4c71774bbbc9..7629afc43226 100644
--- a/scripts/Makefile.policy
+++ b/scripts/Makefile.policy
@@ -23,7 +23,11 @@ endif
 # ---------------------------------------------------------------------------
 
 $(obj)/%.sconfig.tmp: $(src)/%.sconfig FORCE
+ifeq ($(KPOLICY_TMPUPDATE),)
 	$(call filechk,cat)
+else
+	$(call if_changed,shipped)
+endif
 
 quiet_cmd_sconfigpost_c = SCONFPP $@
       cmd_sconfigpost_c = $(SCONFIGPOST) -o $@ -D$(depfile) $(2)
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 06/17] defaultenv: update PS1 according to security policy
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 05/17] kbuild: allow security config use without source tree modification Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 07/17] security: policy: support externally provided configs Ahmad Fatoum
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@barebox.org>

This nifty optional feature makes it easy to see what security policy
is currently active.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Documentation/user/defaultenv-2.rst           |  2 ++
 common/Kconfig                                |  5 +++++
 defaultenv/Makefile                           |  1 +
 .../bin/ps1-policy                            | 20 +++++++++++++++++++
 .../init/ps1-policy                           |  1 +
 .../init/source-colors                        |  1 +
 defaultenv/defaultenv.c                       |  2 ++
 7 files changed, 32 insertions(+)
 create mode 100755 defaultenv/defaultenv-2-security-policy/bin/ps1-policy
 create mode 100644 defaultenv/defaultenv-2-security-policy/init/ps1-policy
 create mode 100644 defaultenv/defaultenv-2-security-policy/init/source-colors

diff --git a/Documentation/user/defaultenv-2.rst b/Documentation/user/defaultenv-2.rst
index a01a70fa9310..3715fd770d9d 100644
--- a/Documentation/user/defaultenv-2.rst
+++ b/Documentation/user/defaultenv-2.rst
@@ -23,6 +23,7 @@ The default environment is composed from different directories during compilatio
   defaultenv/defaultenv-2-dfu                  -> overlay for DFU
   defaultenv/defaultenv-2-reboot-mode          -> overlay for reboot modes
   defaultenv/defaultenv-2-menu                 -> overlay for menus
+  defaultenv/defaultenv-2-security-policy       -> overlay for security policy
   arch/$ARCH/boards/<board>/defaultenv-<board> -> board specific overlay
   $(CONFIG_DEFAULT_ENVIRONMENT_PATH)           -> config specific overlay
 
@@ -43,6 +44,7 @@ and their respective included directories in ``defaultenv/Makefile``:
   bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_MENU) += defaultenv-2-menu
   bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_DFU) += defaultenv-2-dfu
   bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE) += defaultenv-2-reboot-mode
+  bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_SECURITY_POLICY) += defaultenv-2-security-policy
   bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-1
 
 /env/bin/init
diff --git a/common/Kconfig b/common/Kconfig
index 67aff85efc9b..0c7e5a10c38e 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1087,6 +1087,11 @@ config DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE
 	depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
 	depends on REBOOT_MODE
 
+config DEFAULT_ENVIRONMENT_GENERIC_NEW_SECURITY_POLICY
+	bool "Update PS1 according to active security policy"
+	depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
+	depends on SECURITY_POLICY
+
 config DEFAULT_ENVIRONMENT_GENERIC_NEW_IKCONFIG
 	bool "Ship .config as /env/data/config"
 	depends on DEFAULT_ENVIRONMENT_GENERIC_NEW
diff --git a/defaultenv/Makefile b/defaultenv/Makefile
index 1eaca9f41087..ab88ed739305 100644
--- a/defaultenv/Makefile
+++ b/defaultenv/Makefile
@@ -4,6 +4,7 @@ bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW) += defaultenv-2-base
 bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_MENU) += defaultenv-2-menu
 bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_DFU) += defaultenv-2-dfu
 bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE) += defaultenv-2-reboot-mode
+bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_SECURITY_POLICY) += defaultenv-2-security-policy
 bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-1
 obj-$(CONFIG_DEFAULT_ENVIRONMENT) += defaultenv.o
 extra-y += barebox_default_env barebox_default_env.h barebox_default_env$(DEFAULT_COMPRESSION_SUFFIX) barebox_zero_env
diff --git a/defaultenv/defaultenv-2-security-policy/bin/ps1-policy b/defaultenv/defaultenv-2-security-policy/bin/ps1-policy
new file mode 100755
index 000000000000..af121bbbaeed
--- /dev/null
+++ b/defaultenv/defaultenv-2-security-policy/bin/ps1-policy
@@ -0,0 +1,20 @@
+if [ -n "$security.policy" ]; then
+	if [ "$security.policy" = "devel" ]; then
+		PS1_policy_color="$YELLOW"
+	elif [ "$security.policy" = "factory" ]; then
+		PS1_policy_color="$PINK"
+	elif [ "$security.policy" = "lockdown" ]; then
+		PS1_policy_color="$RED;"
+	elif [ "$security.policy" = "tamper" ]; then
+		PS1_policy_color="$RED_INV";
+	elif [ "$security.policy" = "return" ]; then
+		PS1_policy_color="$PINK_INV";
+	elif [[ "$security.policy" = "debug*" ]]; then
+		PS1_policy_color="$CYAN";
+        else
+                PS1_policy_color="$NC"
+	fi
+
+	. /env/init/ps1
+	export PS1="(${PS1_policy_color}${security.policy}${NC}) $PS1"
+fi
diff --git a/defaultenv/defaultenv-2-security-policy/init/ps1-policy b/defaultenv/defaultenv-2-security-policy/init/ps1-policy
new file mode 100644
index 000000000000..a76cd7fd1728
--- /dev/null
+++ b/defaultenv/defaultenv-2-security-policy/init/ps1-policy
@@ -0,0 +1 @@
+PROMPT_COMMAND=". /env/bin/ps1-policy"
diff --git a/defaultenv/defaultenv-2-security-policy/init/source-colors b/defaultenv/defaultenv-2-security-policy/init/source-colors
new file mode 100644
index 000000000000..3df8e02b0ebc
--- /dev/null
+++ b/defaultenv/defaultenv-2-security-policy/init/source-colors
@@ -0,0 +1 @@
+. /env/data/ansi-colors
diff --git a/defaultenv/defaultenv.c b/defaultenv/defaultenv.c
index 2567653751cb..d6cca466a683 100644
--- a/defaultenv/defaultenv.c
+++ b/defaultenv/defaultenv.c
@@ -49,6 +49,8 @@ static void defaultenv_add_base(void)
 		defaultenv_append_directory(defaultenv_2_dfu);
 	if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE))
 		defaultenv_append_directory(defaultenv_2_reboot_mode);
+	if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_SECURITY_POLICY))
+		defaultenv_append_directory(defaultenv_2_security_policy);
 	if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_IKCONFIG))
 		defaultenv_append_directory(defaultenv_2_ikconfig);
 	if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC))
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 07/17] security: policy: support externally provided configs
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 06/17] defaultenv: update PS1 according to security policy Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 08/17] commands: implement sconfig command Ahmad Fatoum
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@barebox.org>

The enforcement of security policies to be up-to-date and removal of
implicit syncing nudges users into checking in the actual security
policy into version control. To allow the policies to live outside the
barebox tree, introduce CONFIG_SECURITY_POLICY_PATH that takes a
space-separated list of configs.

For now, the option is very strict: All files referenced must be placed
into security/ in the barebox source directory. Different build rules
sharing the same source directory can install their configs with
different names and customize via CONFIG_SECURITY_POLICY_PATH which options
to include.

sconfigpost also supports iterating over directories, but this feature
is left out for now, as it needs more extensive testing to verify that
targets are rebuilt as often as needed and not more.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 security/Kconfig.policy | 15 +++++++++++++++
 security/Makefile       | 36 ++++++++++++++++++++++++++++++++++++
 security/policy.c       |  3 +++
 3 files changed, 54 insertions(+)

diff --git a/security/Kconfig.policy b/security/Kconfig.policy
index 6c5cb5687c17..36875ea198d1 100644
--- a/security/Kconfig.policy
+++ b/security/Kconfig.policy
@@ -80,6 +80,21 @@ config SECURITY_POLICY_DEFAULT_PANIC
 
 endchoice
 
+config SECURITY_POLICY_PATH
+	string
+	depends on SECURITY_POLICY
+	prompt "Paths to additional security policies"
+	help
+	  Space separated list of security policies that should be
+	  compiled into barebox and registered. This option currently
+	  requires security policies to have the .sconfig extension
+	  and be located in the barebox source tree's security/
+	  directory.
+	  If left empty, only security policies explicitly provided
+	  and registered by board code will be available.
+
+	  Absolute paths are disallowed.
+
 config SECURITY_POLICY_NAMES
 	bool
 
diff --git a/security/Makefile b/security/Makefile
index 16b328266a1b..c7896ed74fb3 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -8,6 +8,9 @@ obj-pbl-$(CONFIG_HAVE_OPTEE)	+= optee.o
 obj-$(CONFIG_BLOBGEN)		+= blobgen.o
 obj-$(CONFIG_PASSWORD)		+= password.o
 
+# Default password handling
+# ---------------------------------------------------------------------------
+#
 ifdef CONFIG_PASSWORD
 
 ifeq ($(CONFIG_PASSWORD_DEFAULT),"")
@@ -29,3 +32,36 @@ include/generated/passwd.h: FORCE
 
 $(obj)/password.o: include/generated/passwd.h
 endif # CONFIG_PASSWORD
+
+# External security policy handling
+# ---------------------------------------------------------------------------
+
+external-policy := $(foreach p, \
+	$(call remove_quotes,$(CONFIG_SECURITY_POLICY_PATH)), $p)
+
+external-policy-tmp := $(addsuffix .tmp,$(external-policy))
+real-external-policy-tmp := $(addprefix $(obj)/,$(external-policy-tmp))
+
+ifneq ($(external-policy),)
+obj-y	+= default.sconfig.o
+extra-y	+= default.sconfig.c
+always-y += policy-list
+$(foreach p, $(external-policy), \
+	$(if $(findstring /,$p),$(error \
+	CONFIG_SECURITY_POLICY_PATH contains path separators.\
+	$(newline)"$p" must be a file name relative to security/)))
+$(foreach p, $(external-policy), \
+	$(if $(wildcard $(srctree)/$(src)/$p),,$(error \
+	CONFIG_SECURITY_POLICY_PATH contains non-existent files.\
+	$(newline)"$p" does not exit in $$(srctree)/security)))
+endif
+
+$(obj)/policy-list: $(addprefix $(src)/,$(external-policy)) FORCE
+	$(call if_changed,gen_order_src)
+
+targets += $(external-policy-tmp)
+
+$(obj)/default.sconfig.c: $(real-external-policy-tmp) FORCE
+	+$(Q)$(foreach p, $(real-external-policy-tmp), \
+		$(call noop_cmd,security_checkconfig,$p) ;)
+	$(call if_changed_dep,sconfigpost_c,$(real-external-policy-tmp))
diff --git a/security/policy.c b/security/policy.c
index 10d6148866ab..774e64968cba 100644
--- a/security/policy.c
+++ b/security/policy.c
@@ -238,6 +238,9 @@ static int security_init(void)
 	dev_add_param_string(&security_device, "policy", param_set_readonly,
 			     security_policy_get_name, &policy_name, NULL);
 
+	if (*CONFIG_SECURITY_POLICY_PATH)
+		security_policy_add(default);
+
 	return 0;
 }
 pure_initcall(security_init);
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 08/17] commands: implement sconfig command
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (6 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 07/17] security: policy: support externally provided configs Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 09/17] docs: security-policies: add documentation Ahmad Fatoum
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Sascha Hauer <s.hauer@pengutronix.de>

The sconfig command provides a convenient interface to test the new
security policy support. It allows inspecting available policies
and optionally switching between them and enabling/disabling them
piecewise for interactive testing of code that is gated behind these
security options.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de
Co-developed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 commands/Kconfig          |  23 ++++
 commands/Makefile         |   1 +
 commands/sconfig.c        | 219 ++++++++++++++++++++++++++++++++++++++
 include/security/policy.h |   4 +
 4 files changed, 247 insertions(+)
 create mode 100644 commands/sconfig.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 16b995cb3b7c..5c17bb4e2516 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2277,6 +2277,29 @@ endmenu
 
 menu "Security"
 
+config CMD_SCONFIG
+	depends on SECURITY_POLICY
+	select SECURITY_POLICY_NAMES
+	default y
+	bool "sconfig"
+	help
+	  Interact with security policies. By default, this only
+	  allows read-access and not selection of new policies
+
+config CMD_SCONFIG_MODIFY
+	depends on CMD_SCONFIG
+	select HAS_INSECURE_DEFAULTS
+	bool "support policy modification"
+	help
+	  Say y here if you are developing and want to extend the sconfig
+	  command to support selecting a different active policy and
+	  modifying it.
+
+	  This is an very priviliged operation and the recommendation is
+	  to hardcode policy transitions in board code and not from scripts.
+
+	  Say n here if unsure.
+
 config CMD_AVB_PVALUE
 	depends on OPTEE_AVB_PERSISTENT_VALUES
 	tristate
diff --git a/commands/Makefile b/commands/Makefile
index 9247287ed53a..a4d0ec7b1410 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -164,4 +164,5 @@ obj-$(CONFIG_CMD_STACKSMASH)	+= stacksmash.o
 obj-$(CONFIG_CMD_PARTED)	+= parted.o
 obj-$(CONFIG_CMD_EFI_HANDLE_DUMP)	+= efi_handle_dump.o
 obj-$(CONFIG_CMD_HOST)		+= host.o
+obj-$(CONFIG_CMD_SCONFIG)	+= sconfig.o
 UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/sconfig.c b/commands/sconfig.c
new file mode 100644
index 000000000000..bfe8dcbca060
--- /dev/null
+++ b/commands/sconfig.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/kernel.h>
+#include <command.h>
+#include <environment.h>
+#include <getopt.h>
+#include <globalvar.h>
+#include <linux/ctype.h>
+#include <stdio.h>
+
+#include <security/policy.h>
+#include <security/config.h>
+
+#define RED	"\e[1;31m"
+#define GREEN	"\e[1;32m"
+#define YELLOW	"\e[1;33m"
+#define PINK	"\e[1;35m"
+#define CYAN	"\e[1;36m"
+#define NC	"\e[0m"
+
+static struct security_policy *last_override;
+static int debug_iteration;
+static struct notifier_block sconfig_notifier;
+
+static const char *red, *green, *nc;
+
+static void sconfig_print(const struct security_policy *policy)
+{
+	for (int i = 0; i < SCONFIG_NUM; i++) {
+		bool allow = is_allowed(policy, i);
+
+		printf("[%s%s%s] %s\n", allow ? green : red,
+		       allow ? "✓" : "✗", nc, sconfig_names[i]);
+	}
+}
+
+static int sconfig_command_notify(struct notifier_block *nb,
+				  unsigned long opt, void *unused)
+{
+	bool allow = is_allowed(NULL, opt);
+
+	printf("%s%s%s%s\n", allow ? green : red, allow ? "+" : "-", nc,
+	       sconfig_names[opt]);
+
+	return 0;
+}
+
+static int do_sconfig_flags(int argc, char *argv[])
+{
+	const struct security_policy *policy;
+	const char *set = NULL;
+	int ret = 0, opt;
+
+	while ((opt = getopt(argc, argv, "li:vs:")) > 0) {
+		switch (opt) {
+		case 'l':
+			security_policy_list();
+			break;
+		case 'i':
+			policy = security_policy_get(optarg);
+			if (!policy) {
+				ret = -ENOENT;
+				goto out;
+			}
+
+			sconfig_print(policy);
+			break;
+		case 'v':
+			if (!IS_ENABLED(CONFIG_CMD_SCONFIG_MODIFY))
+				return COMMAND_ERROR_USAGE;
+			sconfig_register_handler(&sconfig_notifier,
+						 sconfig_command_notify);
+			break;
+		case 's':
+			if (!IS_ENABLED(CONFIG_CMD_SCONFIG_MODIFY))
+				return COMMAND_ERROR_USAGE;
+			set = optarg;
+			break;
+		default:
+			ret = COMMAND_ERROR_USAGE;
+			goto out;
+		}
+	}
+
+	if (argc != optind) {
+		ret = COMMAND_ERROR_USAGE;
+		goto out;
+	}
+
+	if (set)
+		ret = security_policy_select(set);
+out:
+	if (sconfig_notifier.notifier_call) {
+		sconfig_unregister_handler(&sconfig_notifier);
+		sconfig_notifier.notifier_call = NULL;
+	}
+
+	return ret;
+}
+
+static struct security_policy *alloc_policy(void)
+{
+	struct security_policy *override;
+
+	override = xmalloc(sizeof(*override));
+
+	if (active_policy)
+		memcpy(override->policy, active_policy->policy,
+		       sizeof(override->policy));
+	else
+		memset(override->policy, 0x1, sizeof(override->policy));
+
+	override->name = xasprintf("debug%u", debug_iteration++);
+	override->chained = false;
+
+	return override;
+}
+
+static void free_policy(struct security_policy *policy)
+{
+	security_policy_unregister_one(policy);
+	free(policy);
+}
+
+static int do_sconfig(int argc, char *argv[])
+{
+	struct security_policy *override;
+	bool allow_color;
+	int i, ret;
+
+	allow_color = !streq_ptr(globalvar_get("allow_color"), "0");
+	if (allow_color) {
+		red = RED;
+		green = GREEN;
+		nc = NC;
+	} else {
+		red = green = nc = "";
+	}
+
+	if (argc == 1) {
+		if (active_policy)
+			printf("Active Policy: %s\n\n", active_policy->name);
+		else
+			printf("No Policy is Active\n\n");
+
+		sconfig_print(NULL);
+		return 0;
+	}
+
+	for (i = 1; i < argc; i++) {
+		if ((*argv[i] != '-' && *argv[i] != '+' ) ||
+		    !strstarts(argv[i] + 1, "SCONFIG_"))
+			return do_sconfig_flags(argc, argv);;
+	}
+
+	if (!IS_ENABLED(CONFIG_CMD_SCONFIG_MODIFY)) {
+		printf("Runtime security policy modification disallowed\n");
+		return COMMAND_ERROR_USAGE;
+	}
+
+	override = alloc_policy();
+
+	for (i = 1; i < argc; i++) {
+		char ch_action = *argv[i];
+		enum security_config_option sopt;
+
+		ret = sconfig_lookup(argv[i] + 1);
+		if (ret < 0) {
+			printf("No such option: %s\n", argv[i] + 1);
+			return ret;
+		}
+
+		sopt = ret;
+
+		switch (ch_action) {
+		case '+':
+			override->policy[sopt] = true;
+			break;
+		case '-':
+			override->policy[sopt] = false;
+			break;
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	__security_policy_register(override);
+
+	ret = security_policy_activate(override);
+	if (ret) {
+		free_policy(override);
+		return ret;
+	}
+
+	free_policy(last_override);
+	last_override = override;
+
+	return 0;
+}
+
+BAREBOX_CMD_HELP_START(sconfig)
+BAREBOX_CMD_HELP_TEXT("Interact with security policies")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-l",  "list registered security policies")
+BAREBOX_CMD_HELP_OPT ("-i POLICY",  "show security options of specified POLICY")
+#ifdef CONFIG_CMD_SCONFIG_MODIFY
+BAREBOX_CMD_HELP_OPT ("-v",  "verbose output: report options as they are modified")
+BAREBOX_CMD_HELP_OPT ("-s POLICY",  "select specified POLICY")
+#endif
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(sconfig)
+        .cmd            = do_sconfig,
+        BAREBOX_CMD_DESC("interact with security policies")
+        BAREBOX_CMD_OPTS("[-vlsi] [<+->CAP]...")
+        BAREBOX_CMD_GROUP(CMD_GRP_SECURITY)
+        BAREBOX_CMD_HELP(cmd_sconfig_help)
+BAREBOX_CMD_END
diff --git a/include/security/policy.h b/include/security/policy.h
index 6f270793bc80..c41220ef3b96 100644
--- a/include/security/policy.h
+++ b/include/security/policy.h
@@ -42,6 +42,10 @@ static inline int __security_policy_register(const struct security_policy policy
 }
 #endif
 
+#ifdef CONFIG_CMD_SCONFIG
+void security_policy_unregister_one(const struct security_policy *policy);
+#endif
+
 #define security_policy_add(name) ({				\
 	extern const struct security_policy __policy_##name[];	\
 	__security_policy_register(__policy_##name);		\
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 09/17] docs: security-policies: add documentation
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (7 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 08/17] commands: implement sconfig command Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 10/17] commands: go: add security config option Ahmad Fatoum
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@barebox.org>

Let's add some first documentation for the newly added security policy
support.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Documentation/devel/devel.rst             |   1 +
 Documentation/devel/security-policies.rst |  89 +++++++++++++++++
 Documentation/user/security-policies.rst  | 110 ++++++++++++++++++++++
 Documentation/user/user-manual.rst        |   1 +
 4 files changed, 201 insertions(+)
 create mode 100644 Documentation/devel/security-policies.rst
 create mode 100644 Documentation/user/security-policies.rst

diff --git a/Documentation/devel/devel.rst b/Documentation/devel/devel.rst
index b90805263bbd..84074d142e03 100644
--- a/Documentation/devel/devel.rst
+++ b/Documentation/devel/devel.rst
@@ -13,6 +13,7 @@ Contents:
    troubleshooting
    filesystems
    background-execution
+   security-policies
    project-ideas
    fuzzing
 
diff --git a/Documentation/devel/security-policies.rst b/Documentation/devel/security-policies.rst
new file mode 100644
index 000000000000..af2faa7352fe
--- /dev/null
+++ b/Documentation/devel/security-policies.rst
@@ -0,0 +1,89 @@
+.. _develop_security-policies:
+
+Security Policies (Developer Manual)
+====================================
+
+Overview
+--------
+
+This document describes how to define new SConfig symbols and integrate them
+into Barebox security policies. SConfig uses the same backend as Kconfig, and
+its configuration files live alongside Kconfig files (e.g. ``common/Sconfig``).
+
+Key principles:
+
+- Except for the name, symbols are always ``bool``.
+- Policies are board-specific and described in ``.sconfig`` files at build-time.
+- Every policy is complete and no implicit defaults are applied by mere building
+- Policy ``.sconfig`` files are post-processed into ``.sconfig.c`` files and
+  then compiled and linked into the final barebox binary.
+
+Creating New Symbols
+--------------------
+
+1. **Add a new symbol** to the appropriate ``Sconfig`` file, such as ``common/Sconfig``:
+
+   .. code-block:: kconfig
+
+      config ENV_HANDLING
+          bool "Allow persisting and loading the environment from storage"
+          depends on $(kconfig-enabled ENV_HANDLING)
+
+2. **Reference it in code** using:
+
+   .. code-block:: c
+
+      #include <security/config.h>
+
+      if (!IS_ALLOWS(SCONFIG_ENV_HANDLING))
+          return -EPERM;
+
+3. **Update policies**:
+
+   Every existing ``.sconfig`` policy must define a value for the new symbol
+   as there are no implicit defaults to ensure every policy explicitly encodes
+   all options in accordance with its security requirements.
+
+   Example in ``lockdown.sconfig``:
+
+   .. code-block:: none
+
+      SCONFIG_ENV_HANDLING=n
+
+   And in ``devel.sconfig``:
+
+   .. code-block:: none
+
+      SCONFIG_ENV_HANDLING=y
+
+Linking Policy Files
+--------------------
+
+Policies must be added to the build using ``policy-y`` in the board’s
+Makefile:
+
+.. code-block:: make
+
+   policy-y += lockdown.sconfig
+
+Tips for Symbol Design
+----------------------
+
+- Avoid naming symbols after board names. Favor functionality.
+- Prefer giving Sconfig symbols the same name as Kconfig symbols, when they
+  address the same goal, but at runtime instead of build-time.
+- When possible, reuse logic in core code by wrapping around
+  ``IS_ALLOWED()`` checks.
+
+Validation & Maintenance
+------------------------
+
+Always run ``make security_olddconfig`` for the security policy reference
+configuration ``virt32_policy_defconfig``::
+
+  export ARCH=arm
+  export CROSS_COMPILE=...
+  make virt32_policy_defconfig
+  make security_olddefconfig
+
+CI also checks this configuration and verifies that it's up-to-date.
diff --git a/Documentation/user/security-policies.rst b/Documentation/user/security-policies.rst
new file mode 100644
index 000000000000..b1002285b414
--- /dev/null
+++ b/Documentation/user/security-policies.rst
@@ -0,0 +1,110 @@
+.. _use_security-policies:
+
+Security Policies (User Manual)
+===============================
+
+Overview
+--------
+
+Barebox supports structured security configuration through **security policies**,
+a runtime configuration mechanism that allows switching between multiple
+predefined security policies (e.g. ``lockdown``, ``devel``),
+depending on operational requirements.
+
+This replaces ad-hoc board code with a clean, reproducible, and
+auditable configuration-based model.
+
+Concepts
+--------
+
+- **SConfig**: A configuration system using the same backend as
+  Kconfig, designed for **runtime security policies**.
+- **Policies**: Named configurations like ``lockdown.sconfig``,
+  ``open.sconfig`` specific to each board.
+
+Usage
+-----
+
+1. **Configure a policy** using menuconfig (or another frontend):
+
+   .. code-block:: shell
+
+      make KPOLICY=arch/arm/boards/myboard/lockdown.sconfig security_oldconfig
+      make sercurity_menuconfig # Iterates over all policies
+
+2. **Configuration files** (e.g. ``lockdown.sconfig``) are in Kconfig
+   format with ``SCONFIG_``-prefixed entries.
+
+3. **Build integration**:
+
+   The sconfig files for the board are placed into the ``security/``
+   directory in the source tree and their file names (without slashes)
+   are added to ``CONFIG_SECURITY_POLICY_PATH``.
+
+   Alternatively, policies can also be be referenced in a board's
+   Makefile::
+
+   .. code-block:: make
+
+      lwl-y       += lowlevel.o
+      obj-y       += board.o
+      policy-y    += myboard-lockdown.sconfig myboard-devel.sconfig
+
+   This latter method can be useful when building multiple boards in
+   the same build, but with different security policies.
+
+4. **Registration**:
+
+   Policies added with ``CONFIG_SECURITY_POLICY_PATH`` are automatically
+   registered for all enabled boards.
+
+   Policies added with policy-y need to be explicitly added by symbol
+   to the set of registered policies in board code:
+
+   .. code-block:: c
+
+     #include <security/policy.h>
+
+     security_policy_add(myboard_lockdown)
+     security_policy_add(myboard_devel)
+
+5. **Runtime selection**:
+
+   In board code, switch to a policy by name:
+
+   .. code-block:: c
+
+     #include <security/policy.h>
+
+     sconfig_set_policy("lockdown");
+
+Differences from Kconfig
+------------------------
+
++-------------------------+------------------------------+-----------------------------+
+| Feature                 | Kconfig                      | SConfig                     |
++=========================+==============================+=============================+
+| Purpose                 | Build-time configuration     | Runtime security policy     |
++-------------------------+------------------------------+-----------------------------+
+| File name               | .config                      | ${policy}.sconfig           |
++-------------------------+------------------------------+-----------------------------+
+| policies per build      | One                          | Multiple                    |
++-------------------------+------------------------------+-----------------------------+
+| Symbol types            | bool, int, string, ... etc.  | bool only                   |
++-------------------------+------------------------------+-----------------------------+
+| Symbol dependencies     | Kconfig symbols              | Both Kconfig and Sconfig    |
+|                         |                              | symbols                     |
++-------------------------+------------------------------+-----------------------------+
+
+Best Practices
+--------------
+
+- Maintain all ``.sconfig`` files under version control,
+  either as part of the barebox patch stack or in your BSP
+
+- Document reasoning when changing every single security option
+  (even when doing ``security_olddefconfig``).
+
+- Avoid logic duplication in board code — rely on SConfig conditionals.
+
+- Name policies meaningfully: e.g. ``lockdown``, ``tamper``, ``return``.
diff --git a/Documentation/user/user-manual.rst b/Documentation/user/user-manual.rst
index ce0792000a3c..cb01721863dd 100644
--- a/Documentation/user/user-manual.rst
+++ b/Documentation/user/user-manual.rst
@@ -29,6 +29,7 @@ Contents:
    bootchooser
    remote-control
    security
+   security-policies
    reset-reason
    system-reset
    state
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 10/17] commands: go: add security config option
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (8 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 09/17] docs: security-policies: add documentation Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 11/17] console: ratp: " Ahmad Fatoum
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The go command doesn't do any signature verification and allows
executing arbitrary code. Add a security option, so a policy can disable
this command at runtime.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Sconfig          |  1 +
 commands/Sconfig | 12 ++++++++++++
 commands/go.c    |  4 ++++
 3 files changed, 17 insertions(+)
 create mode 100644 commands/Sconfig

diff --git a/Sconfig b/Sconfig
index ee6ddf53bccb..93f5760ad96f 100644
--- a/Sconfig
+++ b/Sconfig
@@ -5,3 +5,4 @@ mainmenu "Barebox/$(ARCH) Security Configuration"
 source "scripts/Sconfig.include"
 
 source "security/Sconfig"
+source "commands/Sconfig"
diff --git a/commands/Sconfig b/commands/Sconfig
new file mode 100644
index 000000000000..7e6d937e162c
--- /dev/null
+++ b/commands/Sconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "Command Policy"
+
+config CMD_GO
+	bool "Allow go command"
+	depends on $(kconfig-enabled,CMD_GO)
+	help
+	  The go command jumps to an arbitrary address after shutting
+          down barebox and does not do any signature verification.
+
+endmenu
diff --git a/commands/go.c b/commands/go.c
index 3449a2181ad0..640911d90db2 100644
--- a/commands/go.c
+++ b/commands/go.c
@@ -11,6 +11,7 @@
 #include <getopt.h>
 #include <linux/ctype.h>
 #include <errno.h>
+#include <security/config.h>
 
 #define INT_ARGS_MAX	4
 
@@ -24,6 +25,9 @@ static int do_go(int argc, char *argv[])
 	ulong	arg[INT_ARGS_MAX] = {};
 	bool	pass_argv = true;
 
+	if (!IS_ALLOWED(SCONFIG_CMD_GO))
+		return -EPERM;
+
 	while ((opt = getopt(argc, argv, "+si")) > 0) {
 		switch (opt) {
 		case 's':
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 11/17] console: ratp: add security config option
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (9 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 10/17] commands: go: add security config option Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 12/17] bootm: support calling bootm_optional_signed_images at any time Ahmad Fatoum
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

For secure systems that disable the regular console, RATP should be
disabled as well, so add an option to do so.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Sconfig            |  1 +
 common/Sconfig     |  9 +++++++++
 common/console.c   |  4 +++-
 common/ratp/ratp.c | 17 +++++++++++++++++
 4 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 common/Sconfig

diff --git a/Sconfig b/Sconfig
index 93f5760ad96f..899a1fb5783f 100644
--- a/Sconfig
+++ b/Sconfig
@@ -5,4 +5,5 @@ mainmenu "Barebox/$(ARCH) Security Configuration"
 source "scripts/Sconfig.include"
 
 source "security/Sconfig"
+source "common/Sconfig"
 source "commands/Sconfig"
diff --git a/common/Sconfig b/common/Sconfig
new file mode 100644
index 000000000000..479ac5cdf2e5
--- /dev/null
+++ b/common/Sconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "General Settings"
+
+config RATP
+	bool "Allow remote control via RATP"
+	depends on $(kconfig-enabled,CONSOLE_RATP)
+
+endmenu
diff --git a/common/console.c b/common/console.c
index dc552e4c5dac..65e4f1f85224 100644
--- a/common/console.c
+++ b/common/console.c
@@ -5,6 +5,7 @@
  */
 
 #include <config.h>
+#include <security/config.h>
 #include <common.h>
 #include <stdarg.h>
 #include <malloc.h>
@@ -492,7 +493,8 @@ static int getc_raw(void)
 			if (cdev->tstc(cdev)) {
 				int ch = cdev->getc(cdev);
 
-				if (IS_ENABLED(CONFIG_RATP) && ch == 0x01) {
+				if (IS_ENABLED(CONFIG_RATP) && ch == 0x01 &&
+				    IS_ALLOWED(SCONFIG_RATP)) {
 					barebox_ratp(cdev);
 					return -1;
 				}
diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c
index 2906f5a09098..f2735fa88531 100644
--- a/common/ratp/ratp.c
+++ b/common/ratp/ratp.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) "barebox-ratp: " fmt
 
 #include <common.h>
+#include <security/config.h>
 #include <command.h>
 #include <malloc.h>
 #include <init.h>
@@ -46,6 +47,7 @@ struct ratp_ctx {
 
 	struct ratp_bb_pkt *fs_rx;
 
+	struct sconfig_notifier_block sconfig_notifier;
 	struct poller_struct poller;
 	struct work_queue wq;
 
@@ -456,11 +458,22 @@ static void ratp_work_cancel(struct work_struct *w)
 	free(rw);
 }
 
+static void barebox_ratp_sconfig_update(struct sconfig_notifier_block *nb,
+				       enum security_config_option opt,
+				       bool allowed)
+{
+	if (!allowed && ratp_ctx)
+		ratp_unregister(ratp_ctx);
+}
+
 int barebox_ratp(struct console_device *cdev)
 {
 	int ret;
 	struct ratp_ctx *ctx;
 
+	if (!IS_ALLOWED(SCONFIG_RATP))
+		return -EPERM;
+
 	if (!cdev->getc || !cdev->putc)
 		return -EINVAL;
 
@@ -515,6 +528,10 @@ int barebox_ratp(struct console_device *cdev)
 	console_set_active(&ctx->ratp_console, CONSOLE_STDOUT | CONSOLE_STDERR |
 			CONSOLE_STDIN);
 
+	sconfig_register_handler_filtered(&ctx->sconfig_notifier,
+					  barebox_ratp_sconfig_update,
+					  SCONFIG_RATP);
+
 	return 0;
 
 out:
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 12/17] bootm: support calling bootm_optional_signed_images at any time
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (10 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 11/17] console: ratp: " Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 13/17] bootm: make unsigned image support runtime configurable Ahmad Fatoum
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

bootm_init is a late initcall and thus the code has the implicit
assumption that bootm_force_signed_images is called after it runs.

Rework the code to allow calling the function at any time.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 common/bootm.c | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/common/bootm.c b/common/bootm.c
index 3a2eb262b26a..5c129cd4b579 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -116,19 +116,34 @@ static const char * const bootm_verify_names[] = {
 
 static bool force_signed_images = IS_ENABLED(CONFIG_BOOTM_FORCE_SIGNED_IMAGES);
 
-void bootm_force_signed_images(void)
+static void bootm_optional_signed_images(void)
+{
+	/* This function should not be exported */
+	BUG_ON(force_signed_images);
+
+	globalvar_remove("bootm.verify");
+	/* recreate bootm.verify with a single enumeration as option */
+	globalvar_add_simple_enum("bootm.verify", (unsigned int *)&bootm_verify_mode,
+				  bootm_verify_names, ARRAY_SIZE(bootm_verify_names));
+
+	bootm_verify_mode = BOOTM_VERIFY_AVAILABLE;
+}
+
+static void bootm_require_signed_images(void)
 {
 	static unsigned int verify_mode = 0;
 
-	if (force_signed_images)
-		return;
-
 	/* recreate bootm.verify with a single enumeration as option */
 	globalvar_remove("bootm.verify");
 	globalvar_add_simple_enum("bootm.verify", &verify_mode,
 				  &bootm_verify_names[BOOTM_VERIFY_SIGNATURE], 1);
 
 	bootm_verify_mode = BOOTM_VERIFY_SIGNATURE;
+}
+
+void bootm_force_signed_images(void)
+{
+	bootm_require_signed_images();
 	force_signed_images = true;
 }
 
@@ -1075,14 +1090,13 @@ static int bootm_init(void)
 		globalvar_add_simple("bootm.initrd.loadaddr", NULL);
 	}
 
-	if (bootm_signed_images_are_forced())
-		bootm_verify_mode = BOOTM_VERIFY_SIGNATURE;
-
 	globalvar_add_simple_bool("bootm.dryrun", &bootm_dryrun);
 	globalvar_add_simple_int("bootm.verbose", &bootm_verbosity, "%u");
 
-	globalvar_add_simple_enum("bootm.verify", (unsigned int *)&bootm_verify_mode,
-				  bootm_verify_names, ARRAY_SIZE(bootm_verify_names));
+	if (bootm_signed_images_are_forced())
+		bootm_require_signed_images();
+	else
+		bootm_optional_signed_images();
 
 	if (IS_ENABLED(CONFIG_ROOTWAIT_BOOTARG))
 		globalvar_add_simple_int("linux.rootwait",
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 13/17] bootm: make unsigned image support runtime configurable
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (11 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 12/17] bootm: support calling bootm_optional_signed_images at any time Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:06 ` [PATCH RFC 14/17] ARM: configs: add virt32_secure_defconfig Ahmad Fatoum
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

To allow runtime unlocking of a device via security policies, add a new
SCONFIG_BOOT_UNSIGNED_IMAGES option and consult it.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 common/Sconfig | 15 +++++++++++++++
 common/bootm.c | 26 +++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/common/Sconfig b/common/Sconfig
index 479ac5cdf2e5..edbc4bc028af 100644
--- a/common/Sconfig
+++ b/common/Sconfig
@@ -7,3 +7,18 @@ config RATP
 	depends on $(kconfig-enabled,CONSOLE_RATP)
 
 endmenu
+
+menu "Boot Policy"
+
+config BOOT_UNSIGNED_IMAGES
+	bool "Allow booting unsigned images"
+	depends on $(kconfig-enabled,BOOTM_OPTIONAL_SIGNED_IMAGES)
+	help
+	  Say y here if you want to allow booting of images with
+	  an invalid signature or no signature at all.
+
+	  Systems with verified boot chains should say y here
+	  or force it at compile time irrespective of policy
+	  with CONFIG_BOOTM_FORCE_SIGNED_IMAGES
+
+endmenu
diff --git a/common/bootm.c b/common/bootm.c
index 5c129cd4b579..c417e760595a 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -16,8 +16,10 @@
 #include <magicvar.h>
 #include <uncompress.h>
 #include <zero_page.h>
+#include <security/config.h>
 
 static LIST_HEAD(handler_list);
+static struct sconfig_notifier_block sconfig_notifier;
 
 static __maybe_unused struct bootm_overrides bootm_overrides;
 
@@ -114,6 +116,13 @@ static const char * const bootm_verify_names[] = {
 	[BOOTM_VERIFY_SIGNATURE] = "signature",
 };
 
+/*
+ * There's three ways to influence whether signed images are forced:
+ * 1) CONFIG_BOOTM_FORCE_SIGNED_IMAGES: forced at compile time
+ * 2) SCONFIG_BOOT_UNSIGNED_IMAGES: determined by the active security policy
+ * 3) bootm_force_signed_images(): forced dynamically by board code.
+ *                                 will be deprecated in favor of 2)
+ */
 static bool force_signed_images = IS_ENABLED(CONFIG_BOOTM_FORCE_SIGNED_IMAGES);
 
 static void bootm_optional_signed_images(void)
@@ -141,6 +150,16 @@ static void bootm_require_signed_images(void)
 	bootm_verify_mode = BOOTM_VERIFY_SIGNATURE;
 }
 
+static void bootm_unsigned_sconfig_update(struct sconfig_notifier_block *nb,
+					  enum security_config_option opt,
+					  bool allowed)
+{
+	if (!allowed)
+		bootm_require_signed_images();
+	else
+		bootm_optional_signed_images();
+}
+
 void bootm_force_signed_images(void)
 {
 	bootm_require_signed_images();
@@ -149,7 +168,7 @@ void bootm_force_signed_images(void)
 
 bool bootm_signed_images_are_forced(void)
 {
-	return force_signed_images;
+	return force_signed_images || !IS_ALLOWED(SCONFIG_BOOT_UNSIGNED_IMAGES);
 }
 
 static int uimage_part_num(const char *partname)
@@ -1098,6 +1117,11 @@ static int bootm_init(void)
 	else
 		bootm_optional_signed_images();
 
+	sconfig_register_handler_filtered(&sconfig_notifier,
+					  bootm_unsigned_sconfig_update,
+					  SCONFIG_BOOT_UNSIGNED_IMAGES);
+
+
 	if (IS_ENABLED(CONFIG_ROOTWAIT_BOOTARG))
 		globalvar_add_simple_int("linux.rootwait",
 					 &linux_rootwait_secs, "%d");
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 14/17] ARM: configs: add virt32_secure_defconfig
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (12 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 13/17] bootm: make unsigned image support runtime configurable Ahmad Fatoum
@ 2025-08-14 13:06 ` Ahmad Fatoum
  2025-08-14 13:07 ` [PATCH RFC 15/17] boards: qemu-virt: add security policies Ahmad Fatoum
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:06 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The security policy support does not allow for incomplete configs and
thus sconfig files must be refreshed when config options they depend on
changes. This means that a security profile that's up-to-date with
respect to one .config is often outdated with respect to another.

To allow easy development and experimentation, let's make 32-bit ARM
Qemu Virt our reference platform and add a new config for it.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/configs/virt32_secure_defconfig | 301 +++++++++++++++++++++++
 test/arm/virt32_secure_defconfig.yaml    |  20 ++
 2 files changed, 321 insertions(+)
 create mode 100644 arch/arm/configs/virt32_secure_defconfig
 create mode 100644 test/arm/virt32_secure_defconfig.yaml

diff --git a/arch/arm/configs/virt32_secure_defconfig b/arch/arm/configs/virt32_secure_defconfig
new file mode 100644
index 000000000000..34cc49405495
--- /dev/null
+++ b/arch/arm/configs/virt32_secure_defconfig
@@ -0,0 +1,301 @@
+CONFIG_ARCH_VERSATILE=y
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_MACH_VEXPRESS=y
+CONFIG_MACH_VIRT=y
+CONFIG_AEABI=y
+CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
+CONFIG_ARM_EXCEPTIONS_PBL=y
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_SEMIHOSTING=y
+CONFIG_BOOT_ATAGS=y
+CONFIG_ARM_BOOTM_ELF=y
+CONFIG_ARM_BOOTM_FIP=y
+CONFIG_NAME="virt32_secure_defconfig"
+CONFIG_MMU=y
+CONFIG_MALLOC_SIZE=0x0
+CONFIG_KALLSYMS=y
+CONFIG_PROMPT="barebox> "
+CONFIG_HUSH_FANCY_PROMPT=y
+CONFIG_AUTO_COMPLETE=y
+CONFIG_MENU=y
+# CONFIG_TIMESTAMP is not set
+CONFIG_BOOTM_SHOW_TYPE=y
+CONFIG_BOOTM_VERBOSE=y
+CONFIG_BOOTM_INITRD=y
+CONFIG_BOOTM_OFTREE_UIMAGE=y
+CONFIG_BOOTM_AIMAGE=y
+CONFIG_BOOTM_FITIMAGE=y
+CONFIG_BLSPEC=y
+CONFIG_CONSOLE_ALLOW_COLOR=y
+CONFIG_PBL_CONSOLE=y
+CONFIG_CONSOLE_RATP=y
+CONFIG_RATP_CMD_I2C=y
+CONFIG_RATP_CMD_GPIO=y
+CONFIG_PARTITION_DISK_EFI=y
+# CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE is not set
+# CONFIG_PARTITION_DISK_EFI_GPT_COMPARE is not set
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_REBOOT_MODE=y
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_SECURITY_POLICY=y
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW_IKCONFIG=y
+CONFIG_TLV=y
+CONFIG_STATE=y
+CONFIG_BOOTCHOOSER=y
+CONFIG_RESET_SOURCE=y
+CONFIG_MACHINE_ID=y
+CONFIG_SYSTEMD_OF_WATCHDOG=y
+CONFIG_FASTBOOT_SPARSE=y
+CONFIG_FASTBOOT_CMD_OEM=y
+CONFIG_CMD_TUTORIAL=y
+CONFIG_CMD_CLASS=y
+CONFIG_CMD_DEVLOOKUP=y
+CONFIG_CMD_DEVUNBIND=y
+CONFIG_CMD_DMESG=y
+CONFIG_LONGHELP=y
+CONFIG_CMD_IOMEM=y
+CONFIG_CMD_IMD=y
+CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_ARM_MMUINFO=y
+CONFIG_CMD_BLKSTATS=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_CMD_PM_DOMAIN=y
+CONFIG_CMD_NVMEM=y
+CONFIG_CMD_VARINFO=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_MMC_EXTCSD=y
+CONFIG_CMD_POLLER=y
+CONFIG_CMD_SLICE=y
+CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_GO=y
+CONFIG_CMD_LOADB=y
+CONFIG_CMD_LOADS=y
+CONFIG_CMD_LOADY=y
+CONFIG_CMD_RESET=y
+CONFIG_CMD_SAVES=y
+CONFIG_CMD_UIMAGE=y
+CONFIG_CMD_BOOTCHOOSER=y
+CONFIG_CMD_PARTITION=y
+CONFIG_CMD_FINDMNT=y
+CONFIG_CMD_PARTED=y
+CONFIG_CMD_UBIFORMAT=y
+CONFIG_CMD_CREATENV=y
+CONFIG_CMD_EXPORT=y
+CONFIG_CMD_DEFAULTENV=y
+CONFIG_CMD_LOADENV=y
+CONFIG_CMD_PRINTENV=y
+CONFIG_CMD_MAGICVAR=y
+CONFIG_CMD_MAGICVAR_HELP=y
+CONFIG_CMD_SAVEENV=y
+CONFIG_CMD_CMP=y
+CONFIG_CMD_FILETYPE=y
+CONFIG_CMD_LN=y
+CONFIG_CMD_STAT=y
+CONFIG_CMD_MD5SUM=y
+CONFIG_CMD_SHA1SUM=y
+CONFIG_CMD_SHA224SUM=y
+CONFIG_CMD_SHA256SUM=y
+CONFIG_CMD_BASE64=y
+CONFIG_CMD_SHA384SUM=y
+CONFIG_CMD_SHA512SUM=y
+CONFIG_CMD_FIPTOOL=y
+CONFIG_CMD_FIPTOOL_WRITE=y
+CONFIG_CMD_UNCOMPRESS=y
+CONFIG_CMD_LET=y
+CONFIG_CMD_MSLEEP=y
+CONFIG_CMD_READF=y
+CONFIG_CMD_SLEEP=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_TFTP=y
+CONFIG_CMD_IP=y
+CONFIG_CMD_ETHLOG=y
+CONFIG_CMD_ECHO_E=y
+CONFIG_CMD_EDIT=y
+CONFIG_CMD_MENU=y
+CONFIG_CMD_MENU_MANAGEMENT=y
+CONFIG_CMD_MENUTREE=y
+CONFIG_CMD_SPLASH=y
+CONFIG_CMD_FBTEST=y
+CONFIG_CMD_READLINE=y
+CONFIG_CMD_TIMEOUT=y
+CONFIG_CMD_CRC=y
+CONFIG_CMD_CRC_CMP=y
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_MEMTESTER=y
+CONFIG_CMD_MM=y
+CONFIG_CMD_CLK=y
+CONFIG_CMD_DETECT=y
+CONFIG_CMD_TRUNCATE=y
+CONFIG_CMD_SYNC=y
+CONFIG_CMD_FLASH=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_PWM=y
+CONFIG_CMD_LED=y
+CONFIG_CMD_NANDTEST=y
+CONFIG_CMD_NAND_BITFLIP=y
+CONFIG_CMD_POWEROFF=y
+CONFIG_CMD_SMC=y
+CONFIG_CMD_SPI=y
+CONFIG_CMD_LED_TRIGGER=y
+CONFIG_CMD_USBGADGET=y
+CONFIG_CMD_DFU=y
+CONFIG_CMD_WD=y
+CONFIG_CMD_SCONFIG_MODIFY=y
+CONFIG_CMD_BLOBGEN=y
+CONFIG_CMD_LOGIN=y
+CONFIG_CMD_PASSWD=y
+CONFIG_PASSWD_MODE_STAR=y
+CONFIG_CMD_2048=y
+CONFIG_CMD_BAREBOX_UPDATE=y
+CONFIG_CMD_FIRMWARELOAD=y
+CONFIG_CMD_KALLSYMS=y
+CONFIG_CMD_OF_COMPATIBLE=y
+CONFIG_CMD_OF_DIFF=y
+CONFIG_CMD_OF_NODE=y
+CONFIG_CMD_OF_PROPERTY=y
+CONFIG_CMD_OF_DISPLAY_TIMINGS=y
+CONFIG_CMD_OF_FIXUP=y
+CONFIG_CMD_OF_FIXUP_STATUS=y
+CONFIG_CMD_OF_OVERLAY=y
+CONFIG_CMD_OFTREE=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_WATCH=y
+CONFIG_CMD_UPTIME=y
+CONFIG_CMD_TLV=y
+CONFIG_CMD_DHRYSTONE=y
+CONFIG_CMD_SPD_DECODE=y
+CONFIG_CMD_SEED=y
+CONFIG_CMD_STACKSMASH=y
+CONFIG_NET=y
+CONFIG_NET_ETHADDR_FROM_MACHINE_ID=y
+CONFIG_NET_NETCONSOLE=y
+CONFIG_NET_FASTBOOT=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_DEEP_PROBE_DEFAULT=y
+CONFIG_OF_BAREBOX_DRIVERS=y
+CONFIG_OF_BAREBOX_ENV_IN_FS=y
+CONFIG_OF_OVERLAY_LIVE=y
+CONFIG_AIODEV=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_DRIVER_NET_VIRTIO=y
+CONFIG_DRIVER_SPI_GPIO=y
+CONFIG_I2C=y
+CONFIG_I2C_GPIO=y
+CONFIG_I2C_MUX=y
+CONFIG_MTD=y
+CONFIG_MTD_RAW_DEVICE=y
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_DATAFLASH=y
+CONFIG_MTD_M25P80=y
+CONFIG_DRIVER_CFI=y
+CONFIG_NAND=y
+CONFIG_NAND_ALLOW_ERASE_BAD=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_FASTMAP=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_DISK_AHCI=y
+CONFIG_DISK_INTF_PLATFORM_IDE=y
+CONFIG_USB_HOST=y
+CONFIG_USB_DWC2_HOST=y
+CONFIG_USB_DWC2_GADGET=y
+CONFIG_USB_EHCI=y
+CONFIG_USB_ULPI=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_ONBOARD_DEV=y
+CONFIG_TYPEC_TUSB320=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DFU=y
+CONFIG_USB_GADGET_SERIAL=y
+CONFIG_USB_GADGET_FASTBOOT=y
+CONFIG_USB_GADGET_MASS_STORAGE=y
+CONFIG_VIDEO=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_DRIVER_VIDEO_BOCHS_PCI=y
+CONFIG_DRIVER_VIDEO_SIMPLEFB=y
+CONFIG_DRIVER_VIDEO_RAMFB=y
+CONFIG_DRIVER_VIDEO_BACKLIGHT=y
+CONFIG_DRIVER_VIDEO_BACKLIGHT_PWM=y
+CONFIG_DRIVER_VIDEO_SIMPLE_PANEL=y
+CONFIG_MCI=y
+CONFIG_MCI_STARTUP=y
+CONFIG_MCI_MMC_BOOT_PARTITIONS=y
+CONFIG_MCI_DW=y
+CONFIG_MCI_DW_PIO=y
+CONFIG_MCI_MMCI=y
+CONFIG_COMMON_CLK_SCMI=y
+CONFIG_MFD_ACT8846=y
+CONFIG_MFD_DA9063=y
+CONFIG_MFD_MC13XXX=y
+CONFIG_MFD_MC34704=y
+CONFIG_MFD_MC9SDZ60=y
+CONFIG_MFD_STMPE=y
+CONFIG_MFD_STPMIC1=y
+CONFIG_UBOOTVAR=y
+CONFIG_STORAGE_BY_ALIAS=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_LED_PWM=y
+CONFIG_LED_GPIO_OF=y
+CONFIG_LED_TRIGGERS=y
+CONFIG_EEPROM_AT25=y
+CONFIG_EEPROM_AT24=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_INPUT_SPECIALKEYS=y
+CONFIG_VIRTIO_INPUT=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_POLLER=y
+CONFIG_PWM=y
+CONFIG_HWRNG=y
+CONFIG_HW_RANDOM_VIRTIO=y
+CONFIG_GPIO_74164=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_STMPE=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED=y
+CONFIG_REGULATOR_ARM_SCMI=y
+CONFIG_REMOTEPROC=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_PCI_ECAM_GENERIC=y
+CONFIG_ARM_SCMI_PROTOCOL=y
+CONFIG_GENERIC_PHY=y
+CONFIG_USB_NOP_XCEIV=y
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_FS_CRAMFS=y
+CONFIG_FS_EXT4=y
+CONFIG_FS_TFTP=y
+CONFIG_FS_NFS=y
+CONFIG_9P_FS=y
+CONFIG_9P_FS_WRITE=y
+CONFIG_FS_FAT=y
+CONFIG_FS_FAT_WRITE=y
+CONFIG_FS_UBIFS=y
+CONFIG_FS_UBIFS_COMPRESSION_LZO=y
+CONFIG_FS_UBIFS_COMPRESSION_ZLIB=y
+CONFIG_FS_UBIFS_COMPRESSION_ZSTD=y
+CONFIG_FS_BPKFS=y
+CONFIG_FS_UIMAGEFS=y
+CONFIG_FS_SMHFS=y
+CONFIG_FS_PSTORE=y
+CONFIG_FS_PSTORE_CONSOLE=y
+CONFIG_FS_PSTORE_RAMOOPS=y
+CONFIG_FS_SQUASHFS=y
+CONFIG_FS_RATP=y
+CONFIG_FS_UBOOTVARFS=y
+# CONFIG_INSECURE is not set
+CONFIG_SECURITY_POLICY=y
+CONFIG_SECURITY_POLICY_INIT="lockdown"
+CONFIG_SECURITY_POLICY_DEFAULT_PANIC=y
+CONFIG_BUG_ON_DATA_CORRUPTION=y
+CONFIG_DIGEST_SHA1_ARM=y
+CONFIG_DIGEST_SHA256_ARM=y
+CONFIG_CRC8=y
+CONFIG_PNG=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_TER16x32=y
diff --git a/test/arm/virt32_secure_defconfig.yaml b/test/arm/virt32_secure_defconfig.yaml
new file mode 100644
index 000000000000..618cb6a0fb05
--- /dev/null
+++ b/test/arm/virt32_secure_defconfig.yaml
@@ -0,0 +1,20 @@
+targets:
+  main:
+    drivers:
+      QEMUDriver:
+        qemu_bin: qemu-system-arm
+        machine: virt
+        cpu: cortex-a7
+        memory: 1024M
+        kernel: barebox-dt-2nd.img
+        display: qemu-default
+      BareboxDriver:
+        prompt: 'barebox@[^:]+:[^ ]+ '
+        bootstring: 'commandline:'
+      BareboxTestStrategy: {}
+    features:
+      - virtio-mmio
+images:
+  barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img"
+imports:
+  -  ../strategy.py
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 15/17] boards: qemu-virt: add security policies
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (13 preceding siblings ...)
  2025-08-14 13:06 ` [PATCH RFC 14/17] ARM: configs: add virt32_secure_defconfig Ahmad Fatoum
@ 2025-08-14 13:07 ` Ahmad Fatoum
  2025-08-14 13:07 ` [PATCH RFC 16/17] boards: qemu-virt: allow setting policy from command line Ahmad Fatoum
  2025-08-14 13:07 ` [PATCH RFC 17/17] test: py: add basic security policy test Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:07 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@barebox.org>

To make it easier to experiment with security policies, add four example
configurations, two via the build system and two "externally".

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/configs/virt32_secure_defconfig      |  1 +
 common/boards/qemu-virt/Makefile              |  3 +++
 common/boards/qemu-virt/board.c               |  8 +++++++
 .../qemu-virt/qemu-virt-factory.sconfig       | 24 +++++++++++++++++++
 .../qemu-virt/qemu-virt-lockdown.sconfig      | 24 +++++++++++++++++++
 security/qemu-virt-devel.sconfig              | 24 +++++++++++++++++++
 security/qemu-virt-tamper.sconfig             | 24 +++++++++++++++++++
 7 files changed, 108 insertions(+)
 create mode 100644 common/boards/qemu-virt/qemu-virt-factory.sconfig
 create mode 100644 common/boards/qemu-virt/qemu-virt-lockdown.sconfig
 create mode 100644 security/qemu-virt-devel.sconfig
 create mode 100644 security/qemu-virt-tamper.sconfig

diff --git a/arch/arm/configs/virt32_secure_defconfig b/arch/arm/configs/virt32_secure_defconfig
index 34cc49405495..ddfeea5a5066 100644
--- a/arch/arm/configs/virt32_secure_defconfig
+++ b/arch/arm/configs/virt32_secure_defconfig
@@ -292,6 +292,7 @@ CONFIG_FS_UBOOTVARFS=y
 CONFIG_SECURITY_POLICY=y
 CONFIG_SECURITY_POLICY_INIT="lockdown"
 CONFIG_SECURITY_POLICY_DEFAULT_PANIC=y
+CONFIG_SECURITY_POLICY_PATH="qemu-virt-devel.sconfig qemu-virt-tamper.sconfig"
 CONFIG_BUG_ON_DATA_CORRUPTION=y
 CONFIG_DIGEST_SHA1_ARM=y
 CONFIG_DIGEST_SHA256_ARM=y
diff --git a/common/boards/qemu-virt/Makefile b/common/boards/qemu-virt/Makefile
index 30bf4f1955ee..2caa6a20c522 100644
--- a/common/boards/qemu-virt/Makefile
+++ b/common/boards/qemu-virt/Makefile
@@ -9,5 +9,8 @@ ifeq ($(CONFIG_ARM),y)
 DTC_CPP_FLAGS_qemu-virt-flash.dtbo := -DCONFIG_ARM
 endif
 
+policy-y += qemu-virt-factory.sconfig
+policy-y += qemu-virt-lockdown.sconfig
+
 clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z
 clean-files += *.dtbo *.dtbo.S .*.dtso
diff --git a/common/boards/qemu-virt/board.c b/common/boards/qemu-virt/board.c
index 9882b0c31a3c..6f88f24b0690 100644
--- a/common/boards/qemu-virt/board.c
+++ b/common/boards/qemu-virt/board.c
@@ -7,6 +7,7 @@
 #include <init.h>
 #include <of.h>
 #include <deep-probe.h>
+#include <security/policy.h>
 #include "qemu-virt-flash.h"
 
 #ifdef CONFIG_64BIT
@@ -83,6 +84,13 @@ static int virt_board_driver_init(void)
 
 	/* of_probe() will happen later at of_populate_initcall */
 
+	security_policy_add(qemu_virt_factory);
+	security_policy_add(qemu_virt_lockdown);
+	/*
+	 * qemu_virt_devel & qemu_virt_tamper intentionally not added here,
+	 * so the test suite can exercise CONFIG_SECURITY_POLICY_PATH.
+	 */
+
 	return 0;
 }
 postcore_initcall(virt_board_driver_init);
diff --git a/common/boards/qemu-virt/qemu-virt-factory.sconfig b/common/boards/qemu-virt/qemu-virt-factory.sconfig
new file mode 100644
index 000000000000..b19d02b37f7c
--- /dev/null
+++ b/common/boards/qemu-virt/qemu-virt-factory.sconfig
@@ -0,0 +1,24 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Barebox/arm Security Configuration
+#
+SCONFIG_POLICY_NAME="factory"
+SCONFIG_SECURITY_POLICY_SELECT=y
+
+#
+# General Settings
+#
+SCONFIG_RATP=y
+# end of General Settings
+
+#
+# Boot Policy
+#
+# SCONFIG_BOOT_UNSIGNED_IMAGES is not set
+# end of Boot Policy
+
+#
+# Command Policy
+#
+# SCONFIG_CMD_GO is not set
+# end of Command Policy
diff --git a/common/boards/qemu-virt/qemu-virt-lockdown.sconfig b/common/boards/qemu-virt/qemu-virt-lockdown.sconfig
new file mode 100644
index 000000000000..e11e5e069c61
--- /dev/null
+++ b/common/boards/qemu-virt/qemu-virt-lockdown.sconfig
@@ -0,0 +1,24 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Barebox/arm Security Configuration
+#
+SCONFIG_POLICY_NAME="lockdown"
+SCONFIG_SECURITY_POLICY_SELECT=y
+
+#
+# General Settings
+#
+# SCONFIG_RATP is not set
+# end of General Settings
+
+#
+# Boot Policy
+#
+# SCONFIG_BOOT_UNSIGNED_IMAGES is not set
+# end of Boot Policy
+
+#
+# Command Policy
+#
+# SCONFIG_CMD_GO is not set
+# end of Command Policy
diff --git a/security/qemu-virt-devel.sconfig b/security/qemu-virt-devel.sconfig
new file mode 100644
index 000000000000..4513917a95cc
--- /dev/null
+++ b/security/qemu-virt-devel.sconfig
@@ -0,0 +1,24 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Barebox/arm Security Configuration
+#
+SCONFIG_POLICY_NAME="devel"
+SCONFIG_SECURITY_POLICY_SELECT=y
+
+#
+# General Settings
+#
+SCONFIG_RATP=y
+# end of General Settings
+
+#
+# Boot Policy
+#
+SCONFIG_BOOT_UNSIGNED_IMAGES=y
+# end of Boot Policy
+
+#
+# Command Policy
+#
+SCONFIG_CMD_GO=y
+# end of Command Policy
diff --git a/security/qemu-virt-tamper.sconfig b/security/qemu-virt-tamper.sconfig
new file mode 100644
index 000000000000..f2453a9936c1
--- /dev/null
+++ b/security/qemu-virt-tamper.sconfig
@@ -0,0 +1,24 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Barebox/arm Security Configuration
+#
+SCONFIG_POLICY_NAME="tamper"
+# SCONFIG_SECURITY_POLICY_SELECT is not set
+
+#
+# General Settings
+#
+# SCONFIG_RATP is not set
+# end of General Settings
+
+#
+# Boot Policy
+#
+# SCONFIG_BOOT_UNSIGNED_IMAGES is not set
+# end of Boot Policy
+
+#
+# Command Policy
+#
+# SCONFIG_CMD_GO is not set
+# end of Command Policy
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 16/17] boards: qemu-virt: allow setting policy from command line
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (14 preceding siblings ...)
  2025-08-14 13:07 ` [PATCH RFC 15/17] boards: qemu-virt: add security policies Ahmad Fatoum
@ 2025-08-14 13:07 ` Ahmad Fatoum
  2025-08-14 13:07 ` [PATCH RFC 17/17] test: py: add basic security policy test Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:07 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Security policies will normally be selected after consulting efuses,
secure boot status from the EEPROM or unlock tokens.

For easier experimentation in QEMU, allow setting the security policy
via the command line arguments, e.g.:

  pytest --bootarg barebox.security.policy=lockdown

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 common/boards/qemu-virt/Makefile      |  2 +-
 common/boards/qemu-virt/board.c       |  3 ++
 common/boards/qemu-virt/commandline.c | 74 +++++++++++++++++++++++++++
 common/boards/qemu-virt/commandline.h |  9 ++++
 test/arm/virt32_secure_defconfig.yaml |  1 +
 5 files changed, 88 insertions(+), 1 deletion(-)
 create mode 100644 common/boards/qemu-virt/commandline.c
 create mode 100644 common/boards/qemu-virt/commandline.h

diff --git a/common/boards/qemu-virt/Makefile b/common/boards/qemu-virt/Makefile
index 2caa6a20c522..7e1440aecff0 100644
--- a/common/boards/qemu-virt/Makefile
+++ b/common/boards/qemu-virt/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-obj-y += board.o
+obj-y += board.o commandline.o
 obj-y += qemu-virt-flash.dtbo.o fitimage-pubkey.dtb.o
 ifeq ($(CONFIG_RISCV),y)
 DTC_CPP_FLAGS_qemu-virt-flash.dtbo := -DCONFIG_RISCV
diff --git a/common/boards/qemu-virt/board.c b/common/boards/qemu-virt/board.c
index 6f88f24b0690..6ad354218927 100644
--- a/common/boards/qemu-virt/board.c
+++ b/common/boards/qemu-virt/board.c
@@ -9,6 +9,7 @@
 #include <deep-probe.h>
 #include <security/policy.h>
 #include "qemu-virt-flash.h"
+#include "commandline.h"
 
 #ifdef CONFIG_64BIT
 #define MACHINE "virt64"
@@ -91,6 +92,8 @@ static int virt_board_driver_init(void)
 	 * so the test suite can exercise CONFIG_SECURITY_POLICY_PATH.
 	 */
 
+	qemu_virt_parse_commandline(root);
+
 	return 0;
 }
 postcore_initcall(virt_board_driver_init);
diff --git a/common/boards/qemu-virt/commandline.c b/common/boards/qemu-virt/commandline.c
new file mode 100644
index 000000000000..16e4750e123d
--- /dev/null
+++ b/common/boards/qemu-virt/commandline.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define pr_fmt(fmt) "qemu-virt-commandline: " fmt
+
+#include <linux/parser.h>
+#include <of.h>
+#include <string.h>
+#include <security/policy.h>
+#include <xfuncs.h>
+#include <stdio.h>
+#include "commandline.h"
+
+enum {
+	/* String options */
+	Opt_policy,
+	/* Error token */
+	Opt_err
+};
+
+static const match_table_t tokens = {
+	{Opt_policy, "barebox.security.policy=%s"},
+	{Opt_err, NULL}
+};
+
+int qemu_virt_parse_commandline(struct device_node *np)
+{
+	const char *bootargs;
+	char *p, *options, *tmp_options, *policy = NULL;
+	substring_t args[MAX_OPT_ARGS];
+	int ret;
+
+	np = of_get_child_by_name(np, "chosen");
+	if (!np)
+		return -ENOENT;
+
+	ret = of_property_read_string(np, "bootargs", &bootargs);
+	if (ret < 0)
+		return 0;
+
+	options = tmp_options = xstrdup(bootargs);
+
+	while ((p = strsep(&options, " ")) != NULL) {
+		int token;
+
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case Opt_policy:
+			if (!IS_ENABLED(CONFIG_SECURITY_POLICY)) {
+				pr_err("CONFIG_SECURITY_POLICY support is missing\n");
+				continue;
+			}
+
+			policy = match_strdup(&args[0]);
+			if (!policy) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			ret = security_policy_select(policy);
+			if (ret)
+				goto out;
+		default:
+			continue;
+		}
+	}
+
+	ret = 0;
+out:
+	free(policy);
+	free(tmp_options);
+	return ret;
+}
diff --git a/common/boards/qemu-virt/commandline.h b/common/boards/qemu-virt/commandline.h
new file mode 100644
index 000000000000..8759784e07c5
--- /dev/null
+++ b/common/boards/qemu-virt/commandline.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef QEMU_VIRT_COMMANDLINE_H_
+#define QEMU_VIRT_COMMANDLINE_H_
+
+struct device_node;
+
+int qemu_virt_parse_commandline(struct device_node *root);
+
+#endif
diff --git a/test/arm/virt32_secure_defconfig.yaml b/test/arm/virt32_secure_defconfig.yaml
index 618cb6a0fb05..a1537c634811 100644
--- a/test/arm/virt32_secure_defconfig.yaml
+++ b/test/arm/virt32_secure_defconfig.yaml
@@ -7,6 +7,7 @@ targets:
         cpu: cortex-a7
         memory: 1024M
         kernel: barebox-dt-2nd.img
+        boot_args: barebox.security.policy=devel
         display: qemu-default
       BareboxDriver:
         prompt: 'barebox@[^:]+:[^ ]+ '
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC 17/17] test: py: add basic security policy test
  2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
                   ` (15 preceding siblings ...)
  2025-08-14 13:07 ` [PATCH RFC 16/17] boards: qemu-virt: allow setting policy from command line Ahmad Fatoum
@ 2025-08-14 13:07 ` Ahmad Fatoum
  16 siblings, 0 replies; 18+ messages in thread
From: Ahmad Fatoum @ 2025-08-14 13:07 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

This simple test checks that the security policies were added and that a
number of options that we expect to be there indeed change as expected.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 test/arm/virt32_secure_defconfig.yaml |  1 +
 test/py/test_policies.py              | 49 +++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)
 create mode 100644 test/py/test_policies.py

diff --git a/test/arm/virt32_secure_defconfig.yaml b/test/arm/virt32_secure_defconfig.yaml
index a1537c634811..3a26e09ef683 100644
--- a/test/arm/virt32_secure_defconfig.yaml
+++ b/test/arm/virt32_secure_defconfig.yaml
@@ -15,6 +15,7 @@ targets:
       BareboxTestStrategy: {}
     features:
       - virtio-mmio
+      - policies
 images:
   barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img"
 imports:
diff --git a/test/py/test_policies.py b/test/py/test_policies.py
new file mode 100644
index 000000000000..28ab16d2cf68
--- /dev/null
+++ b/test/py/test_policies.py
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+
+
+def test_security_policies(barebox, env):
+    if 'policies' not in env.get_target_features():
+        pytest.skip('policies feature flag missing')
+
+    assert 'Active Policy: devel' in barebox.run_check('sconfig')
+
+    assert set(barebox.run_check('sconfig -l')) == \
+           set(['devel', 'factory', 'lockdown', 'tamper'])
+
+    assert barebox.run_check('varinfo global.bootm.verify') == \
+        ['bootm.verify: available (type: enum) '
+         '(values: "none", "hash", "signature", "available")']
+
+    barebox.run_check('sconfig -s factory')
+    assert 'Active Policy: factory' in barebox.run_check('sconfig')
+
+    stdout = barebox.run_check('sconfig -v -s devel')
+    assert set(['+SCONFIG_BOOT_UNSIGNED_IMAGES',
+                '+SCONFIG_CMD_GO']) <= set(stdout)
+    assert 'Active Policy: devel' in barebox.run_check('sconfig')
+
+    stdout, _, rc = barebox.run('go')
+    assert 'go - start application at address or file' in stdout
+    assert 'go: Operation not permitted' not in stdout
+    assert rc == 1
+
+    stdout = barebox.run_check('sconfig -v -s tamper')
+    assert set(['-SCONFIG_SECURITY_POLICY_SELECT',
+                '-SCONFIG_BOOT_UNSIGNED_IMAGES',
+                '-SCONFIG_RATP',
+                '-SCONFIG_CMD_GO']) <= set(stdout)
+    assert 'Active Policy: tamper' in barebox.run_check('sconfig')
+
+    _, _, rc = barebox.run('sconfig -s devel')
+    assert rc != 0
+    assert 'Active Policy: tamper' in barebox.run_check('sconfig')
+
+    stdout, _, rc = barebox.run('go')
+    assert 'go - start application at address or file' not in stdout
+    assert 'go: Operation not permitted' in stdout
+    assert rc == 127
+
+    assert barebox.run_check('varinfo global.bootm.verify') == \
+        ['bootm.verify: signature (type: enum)']
-- 
2.39.5




^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2025-08-14 15:16 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-14 13:06 [PATCH RFC 00/17] Add security policy support Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 01/17] kconfig: allow setting CONFIG_ from the outside Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 02/17] scripts: include scripts/include for all host tools Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 03/17] kbuild: implement loopable loop_cmd Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 04/17] Add security policy support Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 05/17] kbuild: allow security config use without source tree modification Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 06/17] defaultenv: update PS1 according to security policy Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 07/17] security: policy: support externally provided configs Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 08/17] commands: implement sconfig command Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 09/17] docs: security-policies: add documentation Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 10/17] commands: go: add security config option Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 11/17] console: ratp: " Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 12/17] bootm: support calling bootm_optional_signed_images at any time Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 13/17] bootm: make unsigned image support runtime configurable Ahmad Fatoum
2025-08-14 13:06 ` [PATCH RFC 14/17] ARM: configs: add virt32_secure_defconfig Ahmad Fatoum
2025-08-14 13:07 ` [PATCH RFC 15/17] boards: qemu-virt: add security policies Ahmad Fatoum
2025-08-14 13:07 ` [PATCH RFC 16/17] boards: qemu-virt: allow setting policy from command line Ahmad Fatoum
2025-08-14 13:07 ` [PATCH RFC 17/17] test: py: add basic security policy test Ahmad Fatoum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox