[1/3,V7] Add support for GPG signature enforcement on booted
diff mbox

Message ID 826193491.80726.1471513547015.JavaMail.zimbra@raptorengineeringinc.com
State Accepted
Headers show

Commit Message

Timothy Pearson Aug. 18, 2016, 9:45 a.m. UTC
kernels and related blobs

This can be used to implement a form of organization-controlled secure boot,
whereby kernels may be loaded from a variety of sources but they will only
boot if a valid signature file is found for each component, and only if the
signature is listed in the /etc/pb-lockdown file.

Signed-off-by: Timothy Pearson <tpearson@raptorengineering.com>
---
 configure.ac                  |  63 ++++++++
 discover/Makefile.am          |   3 +-
 discover/boot.c               | 144 ++++++++++++++++---
 discover/boot.h               |  33 +++++
 discover/device-handler.c     |   6 +
 discover/device-handler.h     |   1 +
 discover/grub2/builtins.c     |   8 ++
 discover/kboot-parser.c       |   6 +
 discover/pxe-parser.c         |   7 +
 discover/user-event.c         |  16 ++-
 discover/yaboot-parser.c      |   7 +
 lib/Makefile.am               |  10 +-
 lib/file/file.c               |  77 ++++++++++
 lib/file/file.h               |   3 +
 lib/pb-protocol/pb-protocol.c |  13 ++
 lib/security/gpg.c            | 328 ++++++++++++++++++++++++++++++++++++++++++
 lib/security/gpg.h            |  61 ++++++++
 lib/types/types.h             |   2 +
 ui/common/discover-client.c   |   1 +
 ui/common/discover-client.h   |   1 +
 ui/ncurses/nc-boot-editor.c   |  58 +++++++-
 ui/ncurses/nc-cui.c           |   2 +
 22 files changed, 821 insertions(+), 29 deletions(-)
 create mode 100644 lib/security/gpg.c
 create mode 100644 lib/security/gpg.h

Patch
diff mbox

diff --git a/configure.ac b/configure.ac
index 00a6113..36d3ddb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -170,6 +170,69 @@  AS_IF(
 	]
 )
 
+AC_ARG_WITH(
+	[signed-boot],
+	[AS_HELP_STRING([--with-signed-boot],
+		[build kernel signature checking support [default=yes]]
+	)],
+	[],
+	[with_signed_boot=yes]
+)
+
+AM_CONDITIONAL(
+	[WITH_SIGNED_BOOT],
+	[test "x$with_signed_boot" = "xyes"])
+
+AS_IF(
+	[test "x$with_signed_boot" = "xyes"],
+	[PKG_CHECK_MODULES(
+		[GPGME],
+		[gpgme >= 1.0.0],
+		[SAVE_LIBS="$LIBS" LIBS="$LIBS $gpgme_LIBS"
+			AC_CHECK_LIB(
+				[gpgme],
+				[gpgme_op_verify],
+				[],
+				[AC_MSG_FAILURE([--with-signed-boot was given but the test for gpgme failed.])]
+			)
+			LIBS="$SAVE_LIBS"
+		],
+		[AM_PATH_GPGME([1.0.0], [SAVE_LIBS="$LIBS" LIBS="$LIBS $gpgme_LIBS"
+			AC_CHECK_LIB(
+				[gpgme],
+				[gpgme_op_verify],
+				[],
+				[AC_MSG_FAILURE([--with-signed-boot was given but the test for gpgme failed.])]
+			)
+			LIBS="$SAVE_LIBS"],
+			[AC_MSG_RESULT([$gpgme_PKG_ERRORS])
+				AC_MSG_FAILURE([ Consider adjusting PKG_CONFIG_PATH environment variable])
+			])
+		]
+	)]
+)
+
+AS_IF(
+	[test "x$with_signed_boot" = "xyes"],
+	[SAVE_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $gpgme_CFLAGS"
+		AC_CHECK_HEADERS(
+			[gpgme.h],
+			[],
+			[AC_MSG_FAILURE([ --with-signed-boot given but gpgme.h not found])]
+		)
+		CPPFLAGS="$SAVE_CPPFLAGS"
+	]
+)
+
+AM_CONDITIONAL([WITH_GPGME], [test "x$with_signed_boot" = "xyes"])
+
+AC_ARG_VAR(
+	[lockdown_file],
+	[Location of authorized signature file [default = "/etc/pb-lockdown"]]
+)
+AS_IF([test "x$lockdown_file" = x], [lockdown_file="/etc/pb-lockdown"])
+AC_DEFINE_UNQUOTED(LOCKDOWN_FILE, "$lockdown_file", [Lockdown file location])
+
 AC_ARG_ENABLE(
 	[busybox],
 	[AS_HELP_STRING(
diff --git a/discover/Makefile.am b/discover/Makefile.am
index 899c9a6..4a6cbd0 100644
--- a/discover/Makefile.am
+++ b/discover/Makefile.am
@@ -58,7 +58,8 @@  discover_pb_discover_LDADD = \
 	discover/grub2/grub2-parser.ro \
 	discover/platform.ro \
 	$(core_lib) \
-	$(UDEV_LIBS)
+	$(UDEV_LIBS) \
+	$(GPGME_LIBS)
 
 discover_pb_discover_LDFLAGS = \
 	$(AM_LDFLAGS) \
diff --git a/discover/boot.c b/discover/boot.c
index ba6ce25..1e03784 100644
--- a/discover/boot.c
+++ b/discover/boot.c
@@ -26,27 +26,14 @@ 
 #include "resource.h"
 #include "platform.h"
 
+#include <security/gpg.h>
+
 static const char *boot_hook_dir = PKG_SYSCONF_DIR "/boot.d";
 enum {
 	BOOT_HOOK_EXIT_OK	= 0,
 	BOOT_HOOK_EXIT_UPDATE	= 2,
 };
 
-struct boot_task {
-	struct load_url_result *image;
-	struct load_url_result *initrd;
-	struct load_url_result *dtb;
-	const char *local_image;
-	const char *local_initrd;
-	const char *local_dtb;
-	const char *args;
-	const char *boot_tty;
-	boot_status_fn status_fn;
-	void *status_arg;
-	bool dry_run;
-	bool cancelled;
-};
-
 /**
  * kexec_load - kexec load helper.
  */
@@ -59,20 +46,39 @@  static int kexec_load(struct boot_task *boot_task)
 	char *s_dtb = NULL;
 	char *s_args = NULL;
 
+	boot_task->local_initrd_override = NULL;
+	boot_task->local_dtb_override = NULL;
+	boot_task->local_image_override = NULL;
+
+	if ((result = gpg_validate_boot_files(boot_task))) {
+		if (result == KEXEC_LOAD_SIGNATURE_FAILURE) {
+			pb_log("%s: Aborting kexec due to"
+				" signature verification failure\n", __func__);
+			goto abort_kexec;
+		}
+	}
+
+	const char* local_initrd = (boot_task->local_initrd_override) ?
+		boot_task->local_initrd_override : boot_task->local_initrd;
+	const char* local_dtb = (boot_task->local_dtb_override) ?
+		boot_task->local_dtb_override : boot_task->local_dtb;
+	const char* local_image = (boot_task->local_image_override) ?
+		boot_task->local_image_override : boot_task->local_image;
+
 	p = argv;
 	*p++ = pb_system_apps.kexec;	/* 1 */
 	*p++ = "-l";			/* 2 */
 
-	if (boot_task->local_initrd) {
+	if (local_initrd) {
 		s_initrd = talloc_asprintf(boot_task, "--initrd=%s",
-				boot_task->local_initrd);
+				local_initrd);
 		assert(s_initrd);
 		*p++ = s_initrd;	 /* 3 */
 	}
 
-	if (boot_task->local_dtb) {
+	if (local_dtb) {
 		s_dtb = talloc_asprintf(boot_task, "--dtb=%s",
-						boot_task->local_dtb);
+						local_dtb);
 		assert(s_dtb);
 		*p++ = s_dtb;		 /* 4 */
 	}
@@ -84,7 +90,7 @@  static int kexec_load(struct boot_task *boot_task)
 		*p++ = s_args;		/* 5 */
 	}
 
-	*p++ = boot_task->local_image;	/* 6 */
+	*p++ = local_image;		/* 6 */
 	*p++ = NULL;			/* 7 */
 
 	result = process_run_simple_argv(boot_task, argv);
@@ -92,6 +98,9 @@  static int kexec_load(struct boot_task *boot_task)
 	if (result)
 		pb_log("%s: failed: (%d)\n", __func__, result);
 
+abort_kexec:
+	gpg_validate_boot_files_cleanup(boot_task);
+
 	return result;
 }
 
@@ -378,23 +387,69 @@  static void boot_process(struct load_url_result *result, void *data)
 			check_load(task, "dtb", task->dtb))
 		goto no_load;
 
+	if (task->verify_signature) {
+		if (load_pending(task->image_signature) ||
+				load_pending(task->initrd_signature) ||
+				load_pending(task->dtb_signature) ||
+				load_pending(task->cmdline_signature))
+			return;
+
+		if (check_load(task, "kernel image signature",
+					task->image_signature) ||
+				check_load(task, "initrd signature",
+					task->initrd_signature) ||
+				check_load(task, "dtb signature",
+					task->dtb_signature) ||
+				check_load(task, "command line signature",
+					task->cmdline_signature))
+			goto no_sig_load;
+	}
+
 	/* we make a copy of the local paths, as the boot hooks might update
 	 * and/or create these */
 	task->local_image = task->image ? task->image->local : NULL;
 	task->local_initrd = task->initrd ? task->initrd->local : NULL;
 	task->local_dtb = task->dtb ? task->dtb->local : NULL;
 
+	if (task->verify_signature) {
+		task->local_image_signature = task->image_signature ?
+			task->image_signature->local : NULL;
+		task->local_initrd_signature = task->initrd_signature ?
+			task->initrd_signature->local : NULL;
+		task->local_dtb_signature = task->dtb_signature ?
+			task->dtb_signature->local : NULL;
+		task->local_cmdline_signature = task->cmdline_signature ?
+			task->cmdline_signature->local : NULL;
+	}
+
 	run_boot_hooks(task);
 
 	update_status(task->status_fn, task->status_arg, BOOT_STATUS_INFO,
 			_("performing kexec_load"));
 
 	rc = kexec_load(task);
-	if (rc) {
+	if (rc == KEXEC_LOAD_SIGNATURE_FAILURE) {
+		update_status(task->status_fn, task->status_arg,
+				BOOT_STATUS_ERROR,
+				_("signature verification failed"));
+	}
+	else if (rc == KEXEC_LOAD_SIG_SETUP_INVALID) {
+		update_status(task->status_fn, task->status_arg,
+				BOOT_STATUS_ERROR,
+				_("invalid signature configuration"));
+	}
+	else if (rc) {
 		update_status(task->status_fn, task->status_arg,
-				BOOT_STATUS_ERROR, _("kexec load failed"));
+				BOOT_STATUS_ERROR,
+				_("kexec load failed"));
 	}
 
+no_sig_load:
+	cleanup_load(task->image_signature);
+	cleanup_load(task->initrd_signature);
+	cleanup_load(task->dtb_signature);
+	cleanup_load(task->cmdline_signature);
+
 no_load:
 	cleanup_load(task->image);
 	cleanup_load(task->initrd);
@@ -435,6 +490,8 @@  struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
 		boot_status_fn status_fn, void *status_arg)
 {
 	struct pb_url *image = NULL, *initrd = NULL, *dtb = NULL;
+	struct pb_url *image_sig = NULL, *initrd_sig = NULL, *dtb_sig = NULL,
+		*cmdline_sig = NULL;
 	const struct config *config;
 	struct boot_task *boot_task;
 	const char *boot_desc;
@@ -478,6 +535,8 @@  struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
 	boot_task->status_fn = status_fn;
 	boot_task->status_arg = status_arg;
 
+	boot_task->verify_signature = (lockdown_status() == PB_LOCKDOWN_SIGN);
+
 	if (cmd && cmd->boot_args) {
 		boot_task->args = talloc_strdup(boot_task, cmd->boot_args);
 	} else if (opt && opt->option->boot_args) {
@@ -494,11 +553,52 @@  struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
 		boot_task->boot_tty = config ? config->boot_tty : NULL;
 	}
 
+	if (boot_task->verify_signature) {
+		if (cmd && cmd->args_sig_file) {
+			cmdline_sig = pb_url_parse(opt, cmd->args_sig_file);
+		} else if (opt && opt->args_sig_file) {
+			cmdline_sig = opt->args_sig_file->url;
+		} else {
+			pb_log("%s: no command line signature file"
+				" specified\n", __func__);
+			update_status(status_fn, status_arg, BOOT_STATUS_INFO,
+					_("Boot failed: no command line"
+						" signature file specified"));
+			talloc_free(boot_task);
+			return NULL;
+		}
+	}
+
 	/* start async loads for boot resources */
 	rc = start_url_load(boot_task, "kernel image", image, &boot_task->image)
 	  || start_url_load(boot_task, "initrd", initrd, &boot_task->initrd)
 	  || start_url_load(boot_task, "dtb", dtb, &boot_task->dtb);
 
+	if (boot_task->verify_signature) {
+		/* Generate names of associated signature files and load */
+		if (image) {
+			image_sig = gpg_get_signature_url(ctx, image);
+			rc |= start_url_load(boot_task,
+				"kernel image signature", image_sig,
+				&boot_task->image_signature);
+		}
+		if (initrd) {
+			initrd_sig = gpg_get_signature_url(ctx, initrd);
+			rc |= start_url_load(boot_task, "initrd signature",
+				initrd_sig, &boot_task->initrd_signature);
+		}
+		if (dtb) {
+			dtb_sig = gpg_get_signature_url(ctx, dtb);
+			rc |= start_url_load(boot_task, "dtb signature",
+				dtb_sig, &boot_task->dtb_signature);
+		}
+
+		rc |= start_url_load(boot_task,
+			"kernel command line signature", cmdline_sig,
+			&boot_task->cmdline_signature);
+	}
+
+	/* If all URLs are local, we may be done. */
 	if (rc) {
 		/* Don't call boot_cancel() to preserve the status update */
 		boot_task->cancelled = true;
diff --git a/discover/boot.h b/discover/boot.h
index ec61703..0c84826 100644
--- a/discover/boot.h
+++ b/discover/boot.h
@@ -11,4 +11,37 @@  struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
 		boot_status_fn status_fn, void *status_arg);
 
 void boot_cancel(struct boot_task *task);
+
+struct boot_task {
+	struct load_url_result *image;
+	struct load_url_result *initrd;
+	struct load_url_result *dtb;
+	const char *local_image;
+	const char *local_initrd;
+	const char *local_dtb;
+	char *local_image_override;
+	char *local_initrd_override;
+	char *local_dtb_override;
+	const char *args;
+	const char *boot_tty;
+	boot_status_fn status_fn;
+	void *status_arg;
+	bool dry_run;
+	bool cancelled;
+	bool verify_signature;
+	struct load_url_result *image_signature;
+	struct load_url_result *initrd_signature;
+	struct load_url_result *dtb_signature;
+	struct load_url_result *cmdline_signature;
+	const char *local_image_signature;
+	const char *local_initrd_signature;
+	const char *local_dtb_signature;
+	const char *local_cmdline_signature;
+};
+
+enum {
+	KEXEC_LOAD_SIG_SETUP_INVALID = 253,
+	KEXEC_LOAD_SIGNATURE_FAILURE = 254,
+};
+
 #endif /* _BOOT_H */
diff --git a/discover/device-handler.c b/discover/device-handler.c
index c31fcea..f6b6d22 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -613,6 +613,7 @@  static bool __attribute__((used)) boot_option_is_resolved(
 	return resource_is_resolved(opt->boot_image) &&
 		resource_is_resolved(opt->initrd) &&
 		resource_is_resolved(opt->dtb) &&
+		resource_is_resolved(opt->args_sig_file) &&
 		resource_is_resolved(opt->icon);
 }
 
@@ -638,6 +639,8 @@  static bool boot_option_resolve(struct discover_boot_option *opt,
 	return resource_resolve(opt->boot_image, "boot_image", opt, handler) &&
 		resource_resolve(opt->initrd, "initrd", opt, handler) &&
 		resource_resolve(opt->dtb, "dtb", opt, handler) &&
+		resource_resolve(opt->args_sig_file, "args_sig_file", opt,
+			handler) &&
 		resource_resolve(opt->icon, "icon", opt, handler);
 }
 
@@ -652,6 +655,7 @@  static void boot_option_finalise(struct device_handler *handler,
 	assert(!opt->option->dtb_file);
 	assert(!opt->option->icon_file);
 	assert(!opt->option->device_id);
+	assert(!opt->option->args_sig_file);
 
 	if (opt->boot_image)
 		opt->option->boot_image_file = opt->boot_image->url->full;
@@ -661,6 +665,8 @@  static void boot_option_finalise(struct device_handler *handler,
 		opt->option->dtb_file = opt->dtb->url->full;
 	if (opt->icon)
 		opt->option->icon_file = opt->icon->url->full;
+	if (opt->args_sig_file)
+		opt->option->args_sig_file = opt->args_sig_file->url->full;
 
 	opt->option->device_id = opt->device->device->id;
 
diff --git a/discover/device-handler.h b/discover/device-handler.h
index b6f9fd5..f785ccc 100644
--- a/discover/device-handler.h
+++ b/discover/device-handler.h
@@ -48,6 +48,7 @@  struct discover_boot_option {
 	struct resource		*boot_image;
 	struct resource		*initrd;
 	struct resource		*dtb;
+	struct resource		*args_sig_file;
 	struct resource		*icon;
 };
 
diff --git a/discover/grub2/builtins.c b/discover/grub2/builtins.c
index 8bff732..c16b639 100644
--- a/discover/grub2/builtins.c
+++ b/discover/grub2/builtins.c
@@ -6,7 +6,9 @@ 
 #include <types/types.h>
 #include <talloc/talloc.h>
 #include <util/util.h>
+#include <url/url.h>
 
+#include "discover/resource.h"
 #include "discover/parser.h"
 #include "grub2.h"
 
@@ -69,6 +71,12 @@  static int builtin_linux(struct grub2_script *script,
 		opt->option->boot_args = talloc_asprintf_append(
 						opt->option->boot_args,
 						" %s", argv[i]);
+
+	char* args_sigfile_default = talloc_asprintf(opt,
+		"%s.cmdline.sig", argv[1]);
+	opt->args_sig_file = create_grub2_resource(opt, script->ctx->device,
+						root, args_sigfile_default);
+	talloc_free(args_sigfile_default);
 	return 0;
 }
 
diff --git a/discover/kboot-parser.c b/discover/kboot-parser.c
index cebe787..f7f75e0 100644
--- a/discover/kboot-parser.c
+++ b/discover/kboot-parser.c
@@ -96,6 +96,12 @@  out_add:
 	d_opt->boot_image = create_devpath_resource(d_opt,
 				conf->dc->device, value);
 
+	char* args_sigfile_default = talloc_asprintf(d_opt,
+		"%s.cmdline.sig", value);
+	d_opt->args_sig_file = create_devpath_resource(d_opt,
+				conf->dc->device, args_sigfile_default);
+	talloc_free(args_sigfile_default);
+
 	if (root) {
 		opt->boot_args = talloc_asprintf(opt, "root=%s %s", root, args);
 		talloc_free(args);
diff --git a/discover/pxe-parser.c b/discover/pxe-parser.c
index 4481e5d..4aae8b1 100644
--- a/discover/pxe-parser.c
+++ b/discover/pxe-parser.c
@@ -166,6 +166,13 @@  static void pxe_process_pair(struct conf_context *ctx,
 		url = pxe_url_join(ctx->dc, ctx->dc->conf_url, value);
 		opt->boot_image = create_url_resource(opt, url);
 
+		char* args_sigfile_default = talloc_asprintf(opt,
+			"%s.cmdline.sig", value);
+		url = pxe_url_join(ctx->dc, ctx->dc->conf_url,
+			args_sigfile_default);
+		opt->args_sig_file = create_url_resource(opt, url);
+		talloc_free(args_sigfile_default);
+
 	} else if (streq(name, "INITRD")) {
 		url = pxe_url_join(ctx->dc, ctx->dc->conf_url, value);
 		opt->initrd = create_url_resource(opt, url);
diff --git a/discover/user-event.c b/discover/user-event.c
index 7350b6c..6ea754f 100644
--- a/discover/user-event.c
+++ b/discover/user-event.c
@@ -82,7 +82,7 @@  static void user_event_print_event(struct event __attribute__((unused)) *event)
 }
 
 static struct resource *user_event_resource(struct discover_boot_option *opt,
-		struct event *event)
+		struct event *event, bool gen_boot_args_sigfile)
 {
 	const char *siaddr, *boot_file;
 	struct resource *res;
@@ -101,7 +101,16 @@  static struct resource *user_event_resource(struct discover_boot_option *opt,
 		return NULL;
 	}
 
-	url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr, boot_file);
+	if (gen_boot_args_sigfile) {
+		char* args_sigfile_default = talloc_asprintf(opt,
+			"%s.cmdline.sig", boot_file);
+		url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr,
+			args_sigfile_default);
+		talloc_free(args_sigfile_default);
+	}
+	else
+		url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr,
+			boot_file);
 	url = pb_url_parse(opt, url_str);
 	talloc_free(url_str);
 
@@ -143,12 +152,13 @@  static int parse_user_event(struct discover_context *ctx, struct event *event)
 	opt->id = talloc_asprintf(opt, "%s#%s", dev->id, val);
 	opt->name = talloc_strdup(opt, val);
 
-	d_opt->boot_image = user_event_resource(d_opt, event);
+	d_opt->boot_image = user_event_resource(d_opt, event, false);
 	if (!d_opt->boot_image) {
 		pb_log("%s: no boot image found for %s!\n", __func__,
 				opt->name);
 		goto fail_opt;
 	}
+	d_opt->args_sig_file = user_event_resource(d_opt, event, true);
 
 	val = event_get_param(event, "rootpath");
 	if (val) {
diff --git a/discover/yaboot-parser.c b/discover/yaboot-parser.c
index aa99392..b62f39d 100644
--- a/discover/yaboot-parser.c
+++ b/discover/yaboot-parser.c
@@ -114,6 +114,13 @@  static void yaboot_finish(struct conf_context *conf)
 	/* populate the boot option from state data */
 	state->opt->boot_image = create_yaboot_devpath_resource(state,
 				conf, state->boot_image);
+
+	char* args_sigfile_default = talloc_asprintf(opt,
+		"%s.cmdline.sig", state->boot_image);
+	state->opt->args_sig_file = create_yaboot_devpath_resource(state,
+				conf, args_sigfile_default);
+	talloc_free(args_sigfile_default);
+
 	if (state->initrd) {
 		state->opt->initrd = create_yaboot_devpath_resource(state,
 				conf, state->initrd);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 09bc1aa..bb7dfe4 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -20,6 +20,13 @@  lib_libpbcore_la_CPPFLAGS = \
 	$(AM_CPPFLAGS) \
 	-DPREFIX='"$(prefix)"'
 
+if WITH_GPGME
+gpg_int_SOURCES = lib/security/gpg..h \
+	lib/security/gpg.c
+else
+gpg_int_SOURCES =
+endif
+
 lib_libpbcore_la_SOURCES = \
 	lib/file/file.h \
 	lib/file/file.c \
@@ -50,7 +57,8 @@  lib_libpbcore_la_SOURCES = \
 	lib/util/util.c \
 	lib/util/util.h \
 	lib/flash/config.h \
-	lib/flash/flash.h
+	lib/flash/flash.h \
+	$(gpg_int_SOURCES)
 
 if ENABLE_MTD
 lib_libpbcore_la_SOURCES += \
diff --git a/lib/file/file.c b/lib/file/file.c
index 1bde9fb..6a270a3 100644
--- a/lib/file/file.c
+++ b/lib/file/file.c
@@ -1,5 +1,6 @@ 
 /*
  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
+ *  Copyright (C) 2016 Raptor Engineering, LLC
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -23,11 +24,87 @@ 
 #include <sys/types.h>
 
 #include <talloc/talloc.h>
+#include <log/log.h>
 
 #include "file.h"
 
+#define MAX_FILENAME_SIZE	8192
+#define FILE_XFER_BUFFER_SIZE	8192
+
 static const int max_file_size = 1024 * 1024;
 
+int copy_file_secure_dest(void *ctx,
+	const char *source_file, char **destination_file) {
+	int result = 0;
+	char template[] = "/tmp/petitbootXXXXXX";
+	char dest_filename[MAX_FILENAME_SIZE] = "";
+	FILE *source_handle = fopen(source_file, "r");
+	int destination_fd = mkstemp(template);
+	FILE *destination_handle = fdopen(destination_fd, "w");
+	if (!source_handle || !(destination_handle)) {
+		// handle open error
+		pb_log("%s: failed: unable to open source file '%s'\n",
+			__func__, source_file);
+		return -1;
+	}
+
+	size_t l1;
+	unsigned char *buffer;
+	buffer = talloc_array(ctx, unsigned char, FILE_XFER_BUFFER_SIZE);
+	if (!buffer) {
+		pb_log("%s: failed: unable to allocate file transfer buffer\n",
+			__func__);
+		return -1;
+	}
+
+	/* Copy data */
+	while ((l1 = fread(buffer, 1, sizeof buffer, source_handle)) > 0) {
+		size_t l2 = fwrite(buffer, 1, l1, destination_handle);
+		if (l2 < l1) {
+			if (ferror(destination_handle)) {
+				/* General error */
+				result = -1;
+				pb_log("%s: failed: unknown fault\n", __func__);
+			}
+			else {
+				/* No space on destination device */
+				result = -1;
+				pb_log("%s: failed: temporary storage full\n",
+					__func__);
+			}
+			break;
+		}
+	}
+
+	talloc_free(buffer);
+
+	if (result) {
+		dest_filename[0] = '\0';
+	}
+	else {
+		ssize_t r;
+		char readlink_buffer[MAX_FILENAME_SIZE];
+		snprintf(readlink_buffer, MAX_FILENAME_SIZE, "/proc/self/fd/%d",
+			destination_fd);
+		r = readlink(readlink_buffer, dest_filename,
+			MAX_FILENAME_SIZE);
+		if (r < 0) {
+			/* readlink failed */
+			result = -1;
+			pb_log("%s: failed: unable to obtain temporary filename"
+				"\n", __func__);
+		}
+		dest_filename[r] = '\0';
+	}
+
+	fclose(source_handle);
+	fclose(destination_handle);
+
+	*destination_file = talloc_strdup(ctx, dest_filename);
+
+	return result;
+}
+
 int read_file(void *ctx, const char *filename, char **bufp, int *lenp)
 {
 	struct stat statbuf;
diff --git a/lib/file/file.h b/lib/file/file.h
index 8aa7d3c..a2744a0 100644
--- a/lib/file/file.h
+++ b/lib/file/file.h
@@ -1,5 +1,6 @@ 
 /*
  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
+ *  Copyright (C) 2016 Raptor Engineering, LLC
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -17,6 +18,8 @@ 
 #ifndef FILE_H
 #define FILE_H
 
+int copy_file_secure_dest(void *ctx,
+	const char * source_file, char ** destination_file);
 int read_file(void *ctx, const char *filename, char **bufp, int *lenp);
 int replace_file(const char *filename, char *buf, int len);
 
diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c
index 7887fb0..ad7798e 100644
--- a/lib/pb-protocol/pb-protocol.c
+++ b/lib/pb-protocol/pb-protocol.c
@@ -37,6 +37,7 @@ 
  *    4-byte len, initrd_file
  *    4-byte len, dtb_file
  *    4-byte len, boot_args
+ *    4-byte len, args_sig_file
  *
  * action = 0x2: device remove message
  *  payload:
@@ -49,6 +50,7 @@ 
  *   4-byte len, initrd_file
  *   4-byte len, dtb_file
  *   4-byte len, boot_args
+ *   4-byte len, args_sig_file
  *
  */
 
@@ -72,6 +74,7 @@  void pb_protocol_dump_device(const struct device *dev, const char *text,
 		fprintf(stream, "%s\t\tinit: %s\n", text, opt->initrd_file);
 		fprintf(stream, "%s\t\tdtb:  %s\n", text, opt->dtb_file);
 		fprintf(stream, "%s\t\targs: %s\n", text, opt->boot_args);
+		fprintf(stream, "%s\t\tasig: %s\n", text, opt->args_sig_file);
 	}
 }
 
@@ -197,6 +200,7 @@  int pb_protocol_boot_option_len(const struct boot_option *opt)
 		4 + optional_strlen(opt->initrd_file) +
 		4 + optional_strlen(opt->dtb_file) +
 		4 + optional_strlen(opt->boot_args) +
+		4 + optional_strlen(opt->args_sig_file) +
 		sizeof(opt->is_default);
 }
 
@@ -207,6 +211,7 @@  int pb_protocol_boot_len(const struct boot_command *boot)
 		4 + optional_strlen(boot->initrd_file) +
 		4 + optional_strlen(boot->dtb_file) +
 		4 + optional_strlen(boot->boot_args) +
+		4 + optional_strlen(boot->args_sig_file) + 
 		4 + optional_strlen(boot->tty);
 }
 
@@ -360,6 +365,7 @@  int pb_protocol_serialise_boot_option(const struct boot_option *opt,
 	pos += pb_protocol_serialise_string(pos, opt->initrd_file);
 	pos += pb_protocol_serialise_string(pos, opt->dtb_file);
 	pos += pb_protocol_serialise_string(pos, opt->boot_args);
+	pos += pb_protocol_serialise_string(pos, opt->args_sig_file);
 
 	*(bool *)pos = opt->is_default;
 	pos += sizeof(bool);
@@ -380,6 +386,7 @@  int pb_protocol_serialise_boot_command(const struct boot_command *boot,
 	pos += pb_protocol_serialise_string(pos, boot->initrd_file);
 	pos += pb_protocol_serialise_string(pos, boot->dtb_file);
 	pos += pb_protocol_serialise_string(pos, boot->boot_args);
+	pos += pb_protocol_serialise_string(pos, boot->args_sig_file);
 	pos += pb_protocol_serialise_string(pos, boot->tty);
 
 	assert(pos <= buf + buf_len);
@@ -750,6 +757,9 @@  int pb_protocol_deserialise_boot_option(struct boot_option *opt,
 	if (read_string(opt, &pos, &len, &opt->boot_args))
 		goto out;
 
+	if (read_string(opt, &pos, &len, &opt->args_sig_file))
+		goto out;
+
 	if (len < sizeof(bool))
 		goto out;
 	opt->is_default = *(bool *)(pos);
@@ -785,6 +795,9 @@  int pb_protocol_deserialise_boot_command(struct boot_command *cmd,
 	if (read_string(cmd, &pos, &len, &cmd->boot_args))
 		goto out;
 
+	if (read_string(cmd, &pos, &len, &cmd->args_sig_file))
+		goto out;
+
 	if (read_string(cmd, &pos, &len, &cmd->tty))
 		goto out;
 
diff --git a/lib/security/gpg.c b/lib/security/gpg.c
new file mode 100644
index 0000000..4ef07bf
--- /dev/null
+++ b/lib/security/gpg.c
@@ -0,0 +1,328 @@ 
+/*
+ *  Copyright (C) 2016 Raptor Engineering, LLC
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <file/file.h>
+#include <talloc/talloc.h>
+#include <url/url.h>
+#include <util/util.h>
+#include <i18n/i18n.h>
+
+#include "gpg.h"
+
+struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file)
+{
+	struct pb_url *signature_file = NULL;
+
+	signature_file = pb_url_copy(ctx, base_file);
+	talloc_free(signature_file->file);
+	signature_file->file = talloc_asprintf(signature_file,
+		"%s.sig", base_file->file);
+	talloc_free(signature_file->path);
+	signature_file->path = talloc_asprintf(signature_file,
+		"%s.sig", base_file->path);
+
+	return signature_file;
+}
+
+int verify_file_signature(const char *plaintext_filename,
+	const char *signature_filename, FILE *authorized_signatures_handle,
+	const char *keyring_path)
+{
+	int valid = 0;
+	gpgme_signature_t verification_signatures;
+	gpgme_verify_result_t verification_result;
+	gpgme_data_t plaintext_data;
+	gpgme_data_t signature_data;
+	gpgme_engine_info_t enginfo;
+	gpgme_ctx_t gpg_context;
+	gpgme_error_t err;
+
+	if (signature_filename == NULL)
+		return -1;
+
+	/* Initialize gpgme */
+	setlocale (LC_ALL, "");
+	gpgme_check_version(NULL);
+	gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+	err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: OpenPGP support not available\n", __func__);
+		return -1;
+	}
+	err = gpgme_get_engine_info(&enginfo);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: GPG engine failed to initialize\n", __func__);
+		return -1;
+	}
+	err = gpgme_new(&gpg_context);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: GPG context could not be created\n", __func__);
+		return -1;
+	}
+	err = gpgme_set_protocol(gpg_context, GPGME_PROTOCOL_OpenPGP);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: GPG protocol could not be set\n", __func__);
+		return -1;
+	}
+	if (keyring_path)
+		err = gpgme_ctx_set_engine_info (gpg_context,
+			GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
+			keyring_path);
+	else
+		err = gpgme_ctx_set_engine_info (gpg_context,
+			GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
+			enginfo->home_dir);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: Could not set GPG engine information\n", __func__);
+		return -1;
+	}
+	err = gpgme_data_new_from_file(&plaintext_data, plaintext_filename, 1);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: Could not create GPG plaintext data buffer"
+			" from file '%s'\n", __func__, plaintext_filename);
+		return -1;
+	}
+	err = gpgme_data_new_from_file(&signature_data, signature_filename, 1);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: Could not create GPG signature data buffer"
+			" from file '%s'\n", __func__, signature_filename);
+		return -1;
+	}
+
+	/* Check signature */
+	err = gpgme_op_verify(gpg_context, signature_data, plaintext_data,
+		NULL);
+	if (err != GPG_ERR_NO_ERROR) {
+		pb_log("%s: Could not verify file using GPG signature '%s'\n",
+			__func__, signature_filename);
+		return -1;
+	}
+	verification_result = gpgme_op_verify_result(gpg_context);
+	verification_signatures = verification_result->signatures;
+	while (verification_signatures) {
+		if (verification_signatures->status != GPG_ERR_NO_ERROR) {
+			/* Signature verification failure */
+			pb_log("%s: Signature for key ID '%s' ('%s') invalid."
+				"  Status: %08x\n", __func__,
+				verification_signatures->fpr,
+				signature_filename,
+				verification_signatures->status);
+			verification_signatures = verification_signatures->next;
+			continue;
+		}
+
+		/* Signature check passed with no error */
+		pb_log("%s: Good signature for key ID '%s' ('%s')\n",
+			__func__, verification_signatures->fpr,
+			signature_filename);
+		/* Verify fingerprint is present in
+			* authorized signatures file
+			*/
+		char *auth_sig_line = NULL;
+		size_t auth_sig_len = 0;
+		ssize_t auth_sig_read;
+		rewind(authorized_signatures_handle);
+		while ((auth_sig_read = getline(&auth_sig_line,
+			&auth_sig_len,
+			authorized_signatures_handle)) != -1) {
+			auth_sig_len = strlen(auth_sig_line);
+			while ((auth_sig_line[auth_sig_len-1] == '\n')
+				|| (auth_sig_line[auth_sig_len-1] == '\r'))
+				auth_sig_len--;
+			auth_sig_line[auth_sig_len] = '\0';
+			if (strcmp(auth_sig_line,
+				verification_signatures->fpr) == 0)
+				valid = 1;
+		}
+		free(auth_sig_line);
+		verification_signatures = verification_signatures->next;
+	}
+
+	/* Clean up */
+	gpgme_data_release(plaintext_data);
+	gpgme_data_release(signature_data);
+	gpgme_release(gpg_context);
+
+	if (!valid) {
+		pb_log("%s: Incorrect GPG signature\n", __func__);
+		return -1;
+	}
+
+	pb_log("%s: GPG signature '%s' for file '%s' verified\n",
+		__func__, signature_filename, plaintext_filename);
+
+	return 0;
+}
+
+int gpg_validate_boot_files(struct boot_task *boot_task) {
+	int result = 0;
+	char *kernel_filename = NULL;
+	char *initrd_filename = NULL;
+	char *dtb_filename = NULL;
+
+	FILE *authorized_signatures_handle = NULL;
+
+	char cmdline_template[] = "/tmp/petitbootXXXXXX";
+	int cmdline_fd = mkstemp(cmdline_template);
+	FILE *cmdline_handle = NULL;
+
+	const char* local_initrd_signature = (boot_task->verify_signature) ?
+		boot_task->local_initrd_signature : NULL;
+	const char* local_dtb_signature = (boot_task->verify_signature) ?
+		boot_task->local_dtb_signature : NULL;
+	const char* local_image_signature = (boot_task->verify_signature) ?
+		boot_task->local_image_signature : NULL;
+	const char* local_cmdline_signature = (boot_task->verify_signature) ?
+		boot_task->local_cmdline_signature : NULL;
+
+	if (!boot_task->verify_signature)
+		return result;
+
+	/* Load authorized signatures file */
+	authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r");
+	if (!authorized_signatures_handle) {
+		pb_log("%s: unable to read lockdown file\n", __func__);
+		return KEXEC_LOAD_SIG_SETUP_INVALID;
+	}
+
+	/* Copy files to temporary directory for verification / boot */
+	result = copy_file_secure_dest(boot_task,
+		boot_task->local_image,
+		&kernel_filename);
+	if (result) {
+		pb_log("%s: image copy failed: (%d)\n",
+			__func__, result);
+		return result;
+	}
+	if (boot_task->local_initrd) {
+		result = copy_file_secure_dest(boot_task,
+			boot_task->local_initrd,
+			&initrd_filename);
+		if (result) {
+			pb_log("%s: initrd copy failed: (%d)\n",
+				__func__, result);
+			return result;
+		}
+	}
+	if (boot_task->local_dtb) {
+		result = copy_file_secure_dest(boot_task,
+			boot_task->local_dtb,
+			&dtb_filename);
+		if (result) {
+			pb_log("%s: dtb copy failed: (%d)\n",
+				__func__, result);
+			return result;
+		}
+	}
+	boot_task->local_image_override = talloc_strdup(boot_task,
+		kernel_filename);
+	if (boot_task->local_initrd)
+		boot_task->local_initrd_override = talloc_strdup(boot_task,
+			initrd_filename);
+	if (boot_task->local_dtb)
+		boot_task->local_dtb_override = talloc_strdup(boot_task,
+			dtb_filename);
+
+	/* Write command line to temporary file for verification */
+	if (cmdline_fd < 0) {
+		/* mkstemp failed */
+		pb_log("%s: failed: unable to create command line"
+			" temporary file for verification\n",
+			__func__);
+		result = -1;
+	}
+	else {
+		cmdline_handle = fdopen(cmdline_fd, "w");
+	}
+	if (!cmdline_handle) {
+		/* Failed to open file */
+		pb_log("%s: failed: unable to write command line"
+			" temporary file for verification\n",
+			__func__);
+		result = -1;
+	}
+	else {
+		fwrite(boot_task->args, sizeof(char),
+			strlen(boot_task->args), cmdline_handle);
+		fflush(cmdline_handle);
+	}
+
+	/* Check signatures */
+	if (verify_file_signature(kernel_filename,
+		local_image_signature,
+		authorized_signatures_handle, "/etc/gpg"))
+		result = KEXEC_LOAD_SIGNATURE_FAILURE;
+	if (verify_file_signature(cmdline_template,
+		local_cmdline_signature,
+		authorized_signatures_handle, "/etc/gpg"))
+		result = KEXEC_LOAD_SIGNATURE_FAILURE;
+	if (boot_task->local_initrd_signature)
+		if (verify_file_signature(initrd_filename,
+			local_initrd_signature,
+			authorized_signatures_handle, "/etc/gpg"))
+			result = KEXEC_LOAD_SIGNATURE_FAILURE;
+	if (boot_task->local_dtb_signature)
+		if (verify_file_signature(dtb_filename,
+			local_dtb_signature,
+			authorized_signatures_handle, "/etc/gpg"))
+			result = KEXEC_LOAD_SIGNATURE_FAILURE;
+
+	/* Clean up */
+	if (cmdline_handle) {
+		fclose(cmdline_handle);
+		unlink(cmdline_template);
+	}
+	fclose(authorized_signatures_handle);
+
+	return result;
+}
+
+void gpg_validate_boot_files_cleanup(struct boot_task *boot_task) {
+	if (boot_task->verify_signature) {
+		unlink(boot_task->local_image_override);
+		if (boot_task->local_initrd_override)
+			unlink(boot_task->local_initrd_override);
+		if (boot_task->local_dtb_override)
+			unlink(boot_task->local_dtb_override);
+
+		talloc_free(boot_task->local_image_override);
+		if (boot_task->local_initrd_override)
+			talloc_free(boot_task->local_initrd_override);
+		if (boot_task->local_dtb_override)
+			talloc_free(boot_task->local_dtb_override);
+	}
+}
+
+int lockdown_status() {
+	if (access(LOCKDOWN_FILE, F_OK) == -1)
+		return PB_LOCKDOWN_NONE;
+	else
+		return PB_LOCKDOWN_SIGN;
+}
\ No newline at end of file
diff --git a/lib/security/gpg.h b/lib/security/gpg.h
new file mode 100644
index 0000000..7e59c6a
--- /dev/null
+++ b/lib/security/gpg.h
@@ -0,0 +1,61 @@ 
+/*
+ *  Copyright (C) 2016 Raptor Engineering, LLC
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _PB_GPG_H
+#define _PB_GPG_H
+
+#include <discover/boot.h>
+
+enum {
+	PB_LOCKDOWN_NONE	= 0,
+	PB_LOCKDOWN_SIGN	= 1,
+};
+
+#if defined(HAVE_LIBGPGME)
+#include <gpgme.h>
+#endif /* HAVE_LIBGPGME */
+
+int lockdown_status(void);
+
+struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file);
+
+int verify_file_signature(const char *plaintext_filename,
+	const char *signature_filename, FILE *authorized_signatures_handle,
+	const char *keyring_path);
+
+int gpg_validate_boot_files(struct boot_task *boot_task);
+
+void gpg_validate_boot_files_cleanup(struct boot_task *boot_task);
+
+#if !defined(HAVE_LIBGPGME)
+
+int lockdown_status(void) { return PB_LOCKDOWN_NONE; }
+
+struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file)
+	{ return 0; }
+
+int verify_file_signature(const char *plaintext_filename,
+	const char *signature_filename, FILE *authorized_signatures_handle,
+	const char *keyring_path) { return -1; }
+
+int gpg_validate_boot_files(struct boot_task *boot_task) { return 0; }
+
+void gpg_validate_boot_files_cleanup(struct boot_task *boot_task) { }
+
+#endif /* HAVE_LIBGPGME */
+
+#endif /* _PB_GPG_H */
\ No newline at end of file
diff --git a/lib/types/types.h b/lib/types/types.h
index 5c5f6ed..6b607cd 100644
--- a/lib/types/types.h
+++ b/lib/types/types.h
@@ -52,6 +52,7 @@  struct boot_option {
 	char		*initrd_file;
 	char		*dtb_file;
 	char		*boot_args;
+	char		*args_sig_file;
 	bool		is_default;
 
 	struct list_item	list;
@@ -65,6 +66,7 @@  struct boot_command {
 	char *initrd_file;
 	char *dtb_file;
 	char *boot_args;
+	char *args_sig_file;
 	char *tty;
 };
 
diff --git a/ui/common/discover-client.c b/ui/common/discover-client.c
index 6247dd0..5dbd99b 100644
--- a/ui/common/discover-client.c
+++ b/ui/common/discover-client.c
@@ -312,6 +312,7 @@  static void create_boot_command(struct boot_command *command,
 	command->initrd_file = data->initrd;
 	command->dtb_file = data->dtb;
 	command->boot_args = data->args;
+	command->args_sig_file = data->args_sig_file;
 	command->tty = ttyname(STDIN_FILENO);
 }
 
diff --git a/ui/common/discover-client.h b/ui/common/discover-client.h
index 542a275..59d2df9 100644
--- a/ui/common/discover-client.h
+++ b/ui/common/discover-client.h
@@ -11,6 +11,7 @@  struct pb_boot_data {
 	char *initrd;
 	char *dtb;
 	char *args;
+	char *args_sig_file;
 };
 
 /**
diff --git a/ui/ncurses/nc-boot-editor.c b/ui/ncurses/nc-boot-editor.c
index 4012ec5..13fd6e5 100644
--- a/ui/ncurses/nc-boot-editor.c
+++ b/ui/ncurses/nc-boot-editor.c
@@ -63,6 +63,8 @@  struct boot_editor {
 		struct nc_widget_textbox	*dtb_f;
 		struct nc_widget_label		*args_l;
 		struct nc_widget_textbox	*args_f;
+		struct nc_widget_label		*args_sig_file_l;
+		struct nc_widget_textbox	*args_sig_file_f;
 		struct nc_widget_button		*ok_b;
 		struct nc_widget_button		*help_b;
 		struct nc_widget_button		*cancel_b;
@@ -73,6 +75,9 @@  struct boot_editor {
 	char			*initrd;
 	char			*dtb;
 	char			*args;
+	char			*args_sig_file;
+
+	bool			use_signature_files;
 };
 
 extern const struct help_text boot_editor_help_text;
@@ -198,6 +203,15 @@  static struct pb_boot_data *boot_editor_prepare_data(
 	s = widget_textbox_get_value(boot_editor->widgets.args_f);
 	bd->args = *s ? talloc_strdup(bd, s) : NULL;
 
+	if (boot_editor->use_signature_files) {
+		s = widget_textbox_get_value(
+			boot_editor->widgets.args_sig_file_f);
+		bd->args_sig_file = conditional_prefix(bd, prefix, s);
+	}
+	else {
+		bd->args_sig_file = NULL;
+	}
+
 	return bd;
 }
 
@@ -323,6 +337,12 @@  static void boot_editor_layout_widgets(struct boot_editor *boot_editor)
 	y += layout_pair(boot_editor, y, boot_editor->widgets.args_l,
 					 boot_editor->widgets.args_f);
 
+	if (boot_editor->use_signature_files) {
+		y += layout_pair(boot_editor, y,
+					boot_editor->widgets.args_sig_file_l,
+					boot_editor->widgets.args_sig_file_f);
+	}
+
 
 	y++;
 	widget_move(widget_button_base(boot_editor->widgets.ok_b), y,
@@ -445,6 +465,11 @@  static void boot_editor_find_device(struct boot_editor *boot_editor,
 	if (bd->dtb && !path_on_device(bd_info, bd->dtb))
 		return;
 
+	if (boot_editor->use_signature_files)
+		if (bd->args_sig_file && !path_on_device(bd_info,
+			bd->args_sig_file))
+			return;
+
 	/* ok, we match; preselect the device option, and remove the common
 	 * prefix */
 	boot_editor->selected_device = bd_info->name;
@@ -454,6 +479,9 @@  static void boot_editor_find_device(struct boot_editor *boot_editor,
 		boot_editor->initrd += len;
 	if (boot_editor->dtb)
 		boot_editor->dtb += len;
+	if (boot_editor->use_signature_files)
+		if (boot_editor->args_sig_file)
+			boot_editor->args_sig_file += len;
 }
 
 static void boot_editor_setup_widgets(struct boot_editor *boot_editor,
@@ -501,6 +529,17 @@  static void boot_editor_setup_widgets(struct boot_editor *boot_editor,
 	boot_editor->widgets.args_f = widget_new_textbox(set, 0, 0,
 					field_size, boot_editor->args);
 
+	if (boot_editor->use_signature_files) {
+		boot_editor->widgets.args_sig_file_l = widget_new_label(set,
+				0, 0, _("Argument signature file:"));
+		boot_editor->widgets.args_sig_file_f = widget_new_textbox(set,
+				0, 0, field_size, boot_editor->args_sig_file);
+	}
+	else {
+		boot_editor->widgets.args_sig_file_l = NULL;
+		boot_editor->widgets.args_sig_file_f = NULL;
+	}
+
 	boot_editor->widgets.ok_b = widget_new_button(set, 0, 0, 10,
 					_("OK"), ok_click, boot_editor);
 	boot_editor->widgets.help_b = widget_new_button(set, 0, 0, 10,
@@ -553,6 +592,15 @@  struct boot_editor *boot_editor_init(struct cui *cui,
 	if (!boot_editor)
 		return NULL;
 
+#if defined(HAVE_LIBGPGME)
+	if (access(LOCKDOWN_FILE, F_OK) == -1)
+		boot_editor->use_signature_files = false;
+	else
+		boot_editor->use_signature_files = true;
+#else
+	boot_editor->use_signature_files = false;
+#endif
+
 	talloc_set_destructor(boot_editor, boot_editor_destructor);
 	boot_editor->cui = cui;
 	boot_editor->item = item;
@@ -563,9 +611,10 @@  struct boot_editor *boot_editor_init(struct cui *cui,
 
 	int ncols1 = strncols(_("Device tree:"));
 	int ncols2 = strncols(_("Boot arguments:"));
+	int ncols3 = strncols(_("Argument signature file:"));
 
 	boot_editor->label_x = 1;
-	boot_editor->field_x = 2 + max(ncols1, ncols2);
+	boot_editor->field_x = 2 + max(max(ncols1, ncols2), ncols3);
 
 	nc_scr_init(&boot_editor->scr, pb_boot_editor_sig, 0,
 			cui, boot_editor_process_key,
@@ -584,10 +633,15 @@  struct boot_editor *boot_editor_init(struct cui *cui,
 		boot_editor->initrd = bd->initrd;
 		boot_editor->dtb = bd->dtb;
 		boot_editor->args = bd->args;
+		if (boot_editor->use_signature_files)
+			boot_editor->args_sig_file = bd->args_sig_file;
+		else
+			boot_editor->args_sig_file = talloc_strdup(bd, "");
 		boot_editor_find_device(boot_editor, bd, sysinfo);
 	} else {
 		boot_editor->image = boot_editor->initrd =
-			boot_editor->dtb = boot_editor->args = "";
+			boot_editor->dtb = boot_editor->args =
+			boot_editor->args_sig_file = "";
 	}
 
 	boot_editor->pad = newpad(
diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c
index 0c355cc..09b63b0 100644
--- a/ui/ncurses/nc-cui.c
+++ b/ui/ncurses/nc-cui.c
@@ -543,6 +543,7 @@  static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 	cod->bd->initrd = talloc_strdup(cod->bd, opt->initrd_file);
 	cod->bd->dtb = talloc_strdup(cod->bd, opt->dtb_file);
 	cod->bd->args = talloc_strdup(cod->bd, opt->boot_args);
+	cod->bd->args_sig_file = talloc_strdup(cod->bd, opt->args_sig_file);
 
 	/* This disconnects items array from menu. */
 	result = set_menu_items(cui->main->ncm, NULL);
@@ -566,6 +567,7 @@  static int cui_boot_option_add(struct device *dev, struct boot_option *opt,
 	pb_log("   image  '%s'\n", cod->bd->image);
 	pb_log("   initrd '%s'\n", cod->bd->initrd);
 	pb_log("   args   '%s'\n", cod->bd->args);
+	pb_log("   argsig '%s'\n", cod->bd->args_sig_file);
 
 	/* Re-attach the items array. */
 	result = set_menu_items(cui->main->ncm, cui->main->items);