diff mbox series

[v3,1/4] handlers: add readback handler

Message ID 20200121075553.6677-2-mark.jonas@de.bosch.com
State Accepted
Headers show
Series [v3,1/4] handlers: add readback handler | expand

Commit Message

'Darko Komljenovic' via swupdate Jan. 21, 2020, 7:55 a.m. UTC
From: Kevin Zhang <kevin.zhang3@cn.bosch.com>

To verify that an image was written properly, this readback handler
calculates the sha256 hash of a partition (or part of it) and compares
it against a given hash value.

Signed-off-by: Kevin Zhang <kevin.zhang3@cn.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
---
 handlers/Config.in          |  12 ++++
 handlers/Makefile           |   1 +
 handlers/readback_handler.c | 125 ++++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+)
 create mode 100644 handlers/readback_handler.c

Comments

Stefano Babic Jan. 21, 2020, 8:18 a.m. UTC | #1
On 21/01/20 08:55, 'Mark Jonas' via swupdate wrote:
> From: Kevin Zhang <kevin.zhang3@cn.bosch.com>
> 
> To verify that an image was written properly, this readback handler
> calculates the sha256 hash of a partition (or part of it) and compares
> it against a given hash value.
> 
> Signed-off-by: Kevin Zhang <kevin.zhang3@cn.bosch.com>
> Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
> ---
>  handlers/Config.in          |  12 ++++
>  handlers/Makefile           |   1 +
>  handlers/readback_handler.c | 125 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 138 insertions(+)
>  create mode 100644 handlers/readback_handler.c
> 
> diff --git a/handlers/Config.in b/handlers/Config.in
> index 41eac1c..347552b 100644
> --- a/handlers/Config.in
> +++ b/handlers/Config.in
> @@ -106,6 +106,18 @@ config RDIFFHANDLER
>  	  Add support for applying librsync's rdiff patches,
>  	  see http://librsync.sourcefrog.net/
>  
> +config READBACKHANDLER
> +	bool "readback"
> +	depends on HASH_VERIFY
> +	default n
> +	help
> +	  To verify that an image was written properly, this readback handler
> +	  calculates the sha256 hash of a partition (or part of it) and compares
> +	  it against a given hash value.
> +
> +	  This is a post-install handler running at the same time as
> +	  post-install scripts.
> +
>  config LUASCRIPTHANDLER
>  	bool "Lua Script"
>  	depends on LUA
> diff --git a/handlers/Makefile b/handlers/Makefile
> index 61e4f76..b756f31 100644
> --- a/handlers/Makefile
> +++ b/handlers/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_CFIHAMMING1)+= flash_hamming1_handler.o
>  obj-$(CONFIG_LUASCRIPTHANDLER) += lua_scripthandler.o
>  obj-$(CONFIG_RAW)	+= raw_handler.o
>  obj-$(CONFIG_RDIFFHANDLER) += rdiff_handler.o
> +obj-$(CONFIG_READBACKHANDLER) += readback_handler.o
>  obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o
>  obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o
>  obj-$(CONFIG_SSBLSWITCH) += ssbl_handler.o
> diff --git a/handlers/readback_handler.c b/handlers/readback_handler.c
> new file mode 100644
> index 0000000..a86164f
> --- /dev/null
> +++ b/handlers/readback_handler.c
> @@ -0,0 +1,125 @@
> +/*
> + * SPDX-FileCopyrightText: 2020 Bosch Sicherheitssysteme GmbH
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <linux/fs.h>
> +
> +#include "swupdate.h"
> +#include "handler.h"
> +#include "sslapi.h"
> +#include "util.h"
> +
> +void readback_handler(void);
> +static int readback_postinst(struct img_type *img);
> +
> +static int readback(struct img_type *img, void *data)
> +{
> +	if (!data)
> +		return -1;
> +
> +	script_fn scriptfn = *(script_fn *)data;
> +	switch (scriptfn) {
> +	case POSTINSTALL:
> +		return readback_postinst(img);
> +	case PREINSTALL:
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int readback_postinst(struct img_type *img)
> +{
> +	/* Get property: partition hash */
> +	unsigned char hash[SHA256_HASH_LENGTH];
> +	char *ascii_hash = dict_get_value(&img->properties, "sha256");
> +	if (!ascii_hash || ascii_to_hash(hash, ascii_hash) < 0 || !IsValidHash(hash)) {
> +		ERROR("Invalid hash");
> +		return -EINVAL;
> +	}
> +
> +	/* Get property: partition size */
> +	unsigned int size = 0;
> +	char *value = dict_get_value(&img->properties, "size");
> +	if (value) {
> +		size = strtoul(value, NULL, 10);
> +	} else {
> +		TRACE("Property size not found, use partition size");
> +	}
> +
> +	/* Get property: offset */
> +	unsigned long offset = 0;
> +	value = dict_get_value(&img->properties, "offset");
> +	if (value) {
> +		offset = strtoul(value, NULL, 10);
> +	} else {
> +		TRACE("Property offset not found, use default 0");
> +	}
> +
> +	/* Open the device (partition) */
> +	int fdin = open(img->device, O_RDONLY);
> +	if (fdin < 0) {
> +		ERROR("Failed to open %s: %s", img->device, strerror(errno));
> +		return -ENODEV;
> +	}
> +
> +	/* Get the real size of the partition, if size is not set. */
> +	if (size == 0) {
> +		if (ioctl(fdin, BLKGETSIZE64, &size) < 0) {
> +			ERROR("Cannot get size of %s", img->device);
> +			close(fdin);
> +			return -EFAULT;
> +		}
> +		TRACE("Partition size: %u", size);
> +	}
> +
> +	/* 
> +	 * Seek the file descriptor before passing it to copyfile().
> +	 * This is necessary because copyfile() only accepts streams,
> +	 * so the file descriptor shall be already at the right position.
> +	 */
> +	if (lseek(fdin, offset, SEEK_SET) < 0) {
> +		ERROR("Seek %lu bytes failed: %s", offset, strerror(errno));
> +		close(fdin);
> +		return -EFAULT;
> +	}
> +
> +	/*
> +	 * Perform hash verification. We do not need to pass an output device to
> +	 * the copyfile() function, because we only want it to verify the hash of
> +	 * the input device.
> +	 */
> +	unsigned long offset_out = 0;
> +	int status = copyfile(fdin,
> +			NULL,  /* no output */
> +			size,
> +			&offset_out,
> +			0,     /* no output seek */
> +			1,     /* skip file, do not write to the output */
> +			0,     /* no compressed */
> +			NULL,  /* no checksum */
> +			hash,
> +			0,     /* no encrypted */
> +			NULL); /* no callback */
> +	if (status == 0) {
> +		INFO("Readback verification success");
> +	} else {
> +		ERROR("Readback verification failed, status=%d", status);
> +	}
> +
> +	close(fdin);
> +	return status;
> +}
> +
> +__attribute__((constructor))
> +void readback_handler(void)
> +{
> +	register_handler("readback", readback, SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
> +}
> 

Acked-by: Stefano Babic <sbabic@denx.de>

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/handlers/Config.in b/handlers/Config.in
index 41eac1c..347552b 100644
--- a/handlers/Config.in
+++ b/handlers/Config.in
@@ -106,6 +106,18 @@  config RDIFFHANDLER
 	  Add support for applying librsync's rdiff patches,
 	  see http://librsync.sourcefrog.net/
 
+config READBACKHANDLER
+	bool "readback"
+	depends on HASH_VERIFY
+	default n
+	help
+	  To verify that an image was written properly, this readback handler
+	  calculates the sha256 hash of a partition (or part of it) and compares
+	  it against a given hash value.
+
+	  This is a post-install handler running at the same time as
+	  post-install scripts.
+
 config LUASCRIPTHANDLER
 	bool "Lua Script"
 	depends on LUA
diff --git a/handlers/Makefile b/handlers/Makefile
index 61e4f76..b756f31 100644
--- a/handlers/Makefile
+++ b/handlers/Makefile
@@ -15,6 +15,7 @@  obj-$(CONFIG_CFIHAMMING1)+= flash_hamming1_handler.o
 obj-$(CONFIG_LUASCRIPTHANDLER) += lua_scripthandler.o
 obj-$(CONFIG_RAW)	+= raw_handler.o
 obj-$(CONFIG_RDIFFHANDLER) += rdiff_handler.o
+obj-$(CONFIG_READBACKHANDLER) += readback_handler.o
 obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o
 obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o
 obj-$(CONFIG_SSBLSWITCH) += ssbl_handler.o
diff --git a/handlers/readback_handler.c b/handlers/readback_handler.c
new file mode 100644
index 0000000..a86164f
--- /dev/null
+++ b/handlers/readback_handler.c
@@ -0,0 +1,125 @@ 
+/*
+ * SPDX-FileCopyrightText: 2020 Bosch Sicherheitssysteme GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include "swupdate.h"
+#include "handler.h"
+#include "sslapi.h"
+#include "util.h"
+
+void readback_handler(void);
+static int readback_postinst(struct img_type *img);
+
+static int readback(struct img_type *img, void *data)
+{
+	if (!data)
+		return -1;
+
+	script_fn scriptfn = *(script_fn *)data;
+	switch (scriptfn) {
+	case POSTINSTALL:
+		return readback_postinst(img);
+	case PREINSTALL:
+	default:
+		return 0;
+	}
+}
+
+static int readback_postinst(struct img_type *img)
+{
+	/* Get property: partition hash */
+	unsigned char hash[SHA256_HASH_LENGTH];
+	char *ascii_hash = dict_get_value(&img->properties, "sha256");
+	if (!ascii_hash || ascii_to_hash(hash, ascii_hash) < 0 || !IsValidHash(hash)) {
+		ERROR("Invalid hash");
+		return -EINVAL;
+	}
+
+	/* Get property: partition size */
+	unsigned int size = 0;
+	char *value = dict_get_value(&img->properties, "size");
+	if (value) {
+		size = strtoul(value, NULL, 10);
+	} else {
+		TRACE("Property size not found, use partition size");
+	}
+
+	/* Get property: offset */
+	unsigned long offset = 0;
+	value = dict_get_value(&img->properties, "offset");
+	if (value) {
+		offset = strtoul(value, NULL, 10);
+	} else {
+		TRACE("Property offset not found, use default 0");
+	}
+
+	/* Open the device (partition) */
+	int fdin = open(img->device, O_RDONLY);
+	if (fdin < 0) {
+		ERROR("Failed to open %s: %s", img->device, strerror(errno));
+		return -ENODEV;
+	}
+
+	/* Get the real size of the partition, if size is not set. */
+	if (size == 0) {
+		if (ioctl(fdin, BLKGETSIZE64, &size) < 0) {
+			ERROR("Cannot get size of %s", img->device);
+			close(fdin);
+			return -EFAULT;
+		}
+		TRACE("Partition size: %u", size);
+	}
+
+	/* 
+	 * Seek the file descriptor before passing it to copyfile().
+	 * This is necessary because copyfile() only accepts streams,
+	 * so the file descriptor shall be already at the right position.
+	 */
+	if (lseek(fdin, offset, SEEK_SET) < 0) {
+		ERROR("Seek %lu bytes failed: %s", offset, strerror(errno));
+		close(fdin);
+		return -EFAULT;
+	}
+
+	/*
+	 * Perform hash verification. We do not need to pass an output device to
+	 * the copyfile() function, because we only want it to verify the hash of
+	 * the input device.
+	 */
+	unsigned long offset_out = 0;
+	int status = copyfile(fdin,
+			NULL,  /* no output */
+			size,
+			&offset_out,
+			0,     /* no output seek */
+			1,     /* skip file, do not write to the output */
+			0,     /* no compressed */
+			NULL,  /* no checksum */
+			hash,
+			0,     /* no encrypted */
+			NULL); /* no callback */
+	if (status == 0) {
+		INFO("Readback verification success");
+	} else {
+		ERROR("Readback verification failed, status=%d", status);
+	}
+
+	close(fdin);
+	return status;
+}
+
+__attribute__((constructor))
+void readback_handler(void)
+{
+	register_handler("readback", readback, SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
+}