[OpenWrt-Devel,1/2] base-files: use JSON for storing firmware validation info
diff mbox series

Message ID 20190823061524.28883-1-zajec5@gmail.com
State Accepted
Delegated to: Rafał Miłecki
Headers show
Series
  • [OpenWrt-Devel,1/2] base-files: use JSON for storing firmware validation info
Related show

Commit Message

Rafał Miłecki Aug. 23, 2019, 6:15 a.m. UTC
From: Rafał Miłecki <rafal@milecki.pl>

So far firmware validation result was binary limited: it was either
successful or not. That meant various limitations, e.g.:
1) Lack of proper feedback on validation problems
2) No way of marking firmware as totally broken (impossible to install)

This change introduces JSON for storing detailed validation info. It
provides a list of performed validation tests and their results. It
allows marking firmware as non-forceable (broken image that can't be
even forced to install).
Example:
{
        "tests": {
                "fwtool_signature": true,
                "fwtool_device_match": true
        },
        "valid": true,
        "forceable": true
}

Implementation is based on *internal* check_image bash script that:
1) Uses existing validation functions
2) Provides helpers for setting extra validation info

This allows e.g. platform_check_image() to call notify_check_broken()
when needed & prevent user from bricking a device.

Right now the new JSON info is used by /sbin/sysupgrade only. It's
already a nice gain as it stops users from installing broken images.

Further plans for this feature are:
1) Expose firmware validation using some new ubus method
2) Move validation step from /sbin/sysupgrade into "sysupgrade" ubus
   method so:
   a) It's possible to safely sysupgrade using ubus only
   b) /sbin/sysupgrade can be more like just a CLI

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
 package/base-files/files/sbin/sysupgrade      | 24 +++++---
 .../base-files/files/usr/libexec/check_image  | 59 +++++++++++++++++++
 2 files changed, 74 insertions(+), 9 deletions(-)
 create mode 100755 package/base-files/files/usr/libexec/check_image

Comments

Rafał Miłecki Aug. 30, 2019, 6:34 a.m. UTC | #1
On Fri, 23 Aug 2019 at 08:15, Rafał Miłecki <zajec5@gmail.com> wrote:
> So far firmware validation result was binary limited: it was either
> successful or not. That meant various limitations, e.g.:
> 1) Lack of proper feedback on validation problems
> 2) No way of marking firmware as totally broken (impossible to install)
>
> This change introduces JSON for storing detailed validation info. It
> provides a list of performed validation tests and their results. It
> allows marking firmware as non-forceable (broken image that can't be
> even forced to install).
> Example:
> {
>         "tests": {
>                 "fwtool_signature": true,
>                 "fwtool_device_match": true
>         },
>         "valid": true,
>         "forceable": true
> }
>
> Implementation is based on *internal* check_image bash script that:
> 1) Uses existing validation functions
> 2) Provides helpers for setting extra validation info
>
> This allows e.g. platform_check_image() to call notify_check_broken()
> when needed & prevent user from bricking a device.
>
> Right now the new JSON info is used by /sbin/sysupgrade only. It's
> already a nice gain as it stops users from installing broken images.
>
> Further plans for this feature are:
> 1) Expose firmware validation using some new ubus method
> 2) Move validation step from /sbin/sysupgrade into "sysupgrade" ubus
>    method so:
>    a) It's possible to safely sysupgrade using ubus only
>    b) /sbin/sysupgrade can be more like just a CLI

I decided to:
1) Use more accurate "validate_firmware_image" executable
2) Don't modify /sbin/sysupgrade to use "forceable". That was
extending /sbin/sysupgrade which we should rather avoid in order to
focus on more generic ubus method.

Patch
diff mbox series

diff --git a/package/base-files/files/sbin/sysupgrade b/package/base-files/files/sbin/sysupgrade
index c27c1fbc47..903449d3e5 100755
--- a/package/base-files/files/sbin/sysupgrade
+++ b/package/base-files/files/sbin/sysupgrade
@@ -2,6 +2,7 @@ 
 
 . /lib/functions.sh
 . /lib/functions/system.sh
+. /usr/share/libubox/jshn.sh
 
 # initialize defaults
 export MTD_ARGS=""
@@ -191,9 +192,6 @@  add_overlayfiles() {
 	return 0
 }
 
-# hooks
-sysupgrade_image_check="fwtool_check_signature fwtool_check_image platform_check_image"
-
 if [ $SAVE_OVERLAY = 1 ]; then
 	[ ! -d /overlay/upper/etc ] && {
 		echo "Cannot find '/overlay/upper/etc', required for '-c'" >&2
@@ -316,17 +314,25 @@  case "$IMAGE" in
 		;;
 esac
 
-for check in $sysupgrade_image_check; do
-	( $check "$IMAGE" ) || {
+json_load "$(/usr/libexec/check_image "$IMAGE")" || {
+	echo "Failed to check image"
+	exit 1
+}
+json_get_var valid "valid"
+json_get_var forceable "forceable"
+[ "$valid" -eq 0 ] && {
+	[ "$forceable" -eq 1 ] && {
 		if [ $FORCE -eq 1 ]; then
-			echo "Image check '$check' failed but --force given - will update anyway!" >&2
-			break
+			echo "Image check failed but --force given - will update anyway!" >&2
 		else
-			echo "Image check '$check' failed." >&2
+			echo "Image check failed. Use --force if needed." >&2
 			exit 1
 		fi
+	} || {
+		echo "Image check failed. This firmware can't be installed." >&2
+		exit 1
 	}
-done
+}
 
 if [ -n "$CONF_IMAGE" ]; then
 	case "$(get_magic_word $CONF_IMAGE cat)" in
diff --git a/package/base-files/files/usr/libexec/check_image b/package/base-files/files/usr/libexec/check_image
new file mode 100755
index 0000000000..b9e74c62b2
--- /dev/null
+++ b/package/base-files/files/usr/libexec/check_image
@@ -0,0 +1,59 @@ 
+#!/bin/sh
+
+. /lib/functions.sh
+. /lib/functions/system.sh
+. /usr/share/libubox/jshn.sh
+
+include /lib/upgrade
+
+VALID=1
+FORCEABLE=1
+
+# Mark image as invalid but still possible to install
+notify_check_invalid() {
+	VALID=0
+}
+
+# Mark image as broken (impossible to install)
+notify_check_broken() {
+	VALID=0
+	FORCEABLE=0
+}
+
+# Add result of validation test
+notify_check_test_result() {
+	local old_ns
+
+	json_set_namespace check_image old_ns
+	json_add_boolean "$1" "$2"
+	json_set_namespace $old_ns
+}
+
+err_to_bool() {
+	[ "$1" -ne 0 ] && echo 0 || echo 1
+}
+
+fwtool_check_signature "$1" >&2
+FWTOOL_SIGNATURE=$?
+[ "$FWTOOL_SIGNATURE" -ne 0 ] && notify_check_invalid
+
+fwtool_check_image "$1" >&2
+FWTOOL_DEVICE_MATCH=$?
+[ "$FWTOOL_DEVICE_MATCH" -ne 0 ] && notify_check_invalid
+
+json_set_namespace check_image old_ns
+json_init
+	json_add_object "tests"
+		json_add_boolean fwtool_signature "$(err_to_bool $FWTOOL_SIGNATURE)"
+		json_add_boolean fwtool_device_match "$(err_to_bool $FWTOOL_DEVICE_MATCH)"
+
+		# Call platform_check_image() here so it can add its test
+		# results and still mark image properly.
+		json_set_namespace $old_ns
+		platform_check_image "$1" >&2 || notify_check_invalid
+		json_set_namespace check_image old_ns
+	json_close_object
+	json_add_boolean valid "$VALID"
+	json_add_boolean forceable "$FORCEABLE"
+json_dump -i
+json_set_namespace $old_ns