Add support for GPG signature enforcement on booted
authortpearson@raptorengineering.com <tpearson@raptorengineering.com>
Thu, 18 Aug 2016 09:45:47 +0000 (04:45 -0500)
committerSamuel Mendoza-Jonas <sam@mendozajonas.com>
Fri, 26 Aug 2016 03:23:01 +0000 (13:23 +1000)
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>
Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
(Minor build fixes and gpgme.m4, comment on secure boot in gpg.c)

23 files changed:
configure.ac
discover/Makefile.am
discover/boot.c
discover/boot.h
discover/device-handler.c
discover/device-handler.h
discover/grub2/builtins.c
discover/kboot-parser.c
discover/pxe-parser.c
discover/user-event.c
discover/yaboot-parser.c
lib/Makefile.am
lib/file/file.c
lib/file/file.h
lib/pb-protocol/pb-protocol.c
lib/security/gpg.c [new file with mode: 0644]
lib/security/gpg.h [new file with mode: 0644]
lib/types/types.h
m4/gpgme.m4 [new file with mode: 0644]
ui/common/discover-client.c
ui/common/discover-client.h
ui/ncurses/nc-boot-editor.c
ui/ncurses/nc-cui.c

index 00a61135cfe340d8938f7b131f18585b96561181..41560d1aa237383177c14987965e5b55fe3c9090 100644 (file)
@@ -170,6 +170,69 @@ AS_IF(
        ]
 )
 
        ]
 )
 
+AC_ARG_WITH(
+       [signed-boot],
+       [AS_HELP_STRING([--with-signed-boot],
+               [build kernel signature checking support [default=no]]
+       )],
+       [],
+       [with_signed_boot=no]
+)
+
+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(
 AC_ARG_ENABLE(
        [busybox],
        [AS_HELP_STRING(
index 899c9a63efa231fc41ff87fabf4b49ff235f1d48..4a6cbd094737a3b32ffd3205d844b4c08b0c0563 100644 (file)
@@ -58,7 +58,8 @@ discover_pb_discover_LDADD = \
        discover/grub2/grub2-parser.ro \
        discover/platform.ro \
        $(core_lib) \
        discover/grub2/grub2-parser.ro \
        discover/platform.ro \
        $(core_lib) \
-       $(UDEV_LIBS)
+       $(UDEV_LIBS) \
+       $(GPGME_LIBS)
 
 discover_pb_discover_LDFLAGS = \
        $(AM_LDFLAGS) \
 
 discover_pb_discover_LDFLAGS = \
        $(AM_LDFLAGS) \
index 04e7a543919a819686ca1027ab11dfe628098e5a..c4ddfef591ebf5c76bf483ac0d5fa2fd13c9dc15 100644 (file)
 #include "resource.h"
 #include "platform.h"
 
 #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,
 };
 
 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.
  */
 /**
  * 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;
 
        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 */
 
        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",
                s_initrd = talloc_asprintf(boot_task, "--initrd=%s",
-                               boot_task->local_initrd);
+                               local_initrd);
                assert(s_initrd);
                *p++ = s_initrd;         /* 3 */
        }
 
                assert(s_initrd);
                *p++ = s_initrd;         /* 3 */
        }
 
-       if (boot_task->local_dtb) {
+       if (local_dtb) {
                s_dtb = talloc_asprintf(boot_task, "--dtb=%s",
                s_dtb = talloc_asprintf(boot_task, "--dtb=%s",
-                                               boot_task->local_dtb);
+                                               local_dtb);
                assert(s_dtb);
                *p++ = s_dtb;            /* 4 */
        }
                assert(s_dtb);
                *p++ = s_dtb;            /* 4 */
        }
@@ -82,7 +88,7 @@ static int kexec_load(struct boot_task *boot_task)
        assert(s_args);
        *p++ = s_args;          /* 5 */
 
        assert(s_args);
        *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);
        *p++ = NULL;                    /* 7 */
 
        result = process_run_simple_argv(boot_task, argv);
@@ -90,6 +96,9 @@ static int kexec_load(struct boot_task *boot_task)
        if (result)
                pb_log("%s: failed: (%d)\n", __func__, result);
 
        if (result)
                pb_log("%s: failed: (%d)\n", __func__, result);
 
+abort_kexec:
+       gpg_validate_boot_files_cleanup(boot_task);
+
        return result;
 }
 
        return result;
 }
 
@@ -376,23 +385,69 @@ static void boot_process(struct load_url_result *result, void *data)
                        check_load(task, "dtb", task->dtb))
                goto no_load;
 
                        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;
 
        /* 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);
        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,
                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);
 no_load:
        cleanup_load(task->image);
        cleanup_load(task->initrd);
@@ -433,6 +488,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;
                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;
        const struct config *config;
        struct boot_task *boot_task;
        const char *boot_desc;
@@ -476,6 +533,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->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) {
        if (cmd && cmd->boot_args) {
                boot_task->args = talloc_strdup(boot_task, cmd->boot_args);
        } else if (opt && opt->option->boot_args) {
@@ -492,11 +551,52 @@ struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
                boot_task->boot_tty = config ? config->boot_tty : NULL;
        }
 
                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);
 
        /* 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;
        if (rc) {
                /* Don't call boot_cancel() to preserve the status update */
                boot_task->cancelled = true;
index ec61703144c5409aa77b71de2ce54d254839de0e..21904950c0cb74f077919210876fcb2ce6a1aa45 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef _BOOT_H
 #define _BOOT_H
 
 #ifndef _BOOT_H
 #define _BOOT_H
 
+#include <types/types.h>
+#include "device-handler.h"
+
 struct boot_option;
 struct boot_command;
 
 struct boot_option;
 struct boot_command;
 
@@ -11,4 +14,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);
                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 */
 #endif /* _BOOT_H */
index c31fceaf5a11b1ac22c28f50ad530866f4b7f23e..f6b6d22ed6a2f6f713b7f0d703b42e72901c01d9 100644 (file)
@@ -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) &&
        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);
 }
 
                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) &&
        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);
 }
 
                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->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;
 
        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;
                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;
 
 
        opt->option->device_id = opt->device->device->id;
 
index b6f9fd51a27a7d77cd5f2ca9eef172b12d0bd016..f785cccbe0d494cf9be69179d519cea5ab4df459 100644 (file)
@@ -48,6 +48,7 @@ struct discover_boot_option {
        struct resource         *boot_image;
        struct resource         *initrd;
        struct resource         *dtb;
        struct resource         *boot_image;
        struct resource         *initrd;
        struct resource         *dtb;
+       struct resource         *args_sig_file;
        struct resource         *icon;
 };
 
        struct resource         *icon;
 };
 
index 8bff7328108479f1b95dbd2d3196cdeada238808..c16b6390225aa403820d65d6dd26841282f51407 100644 (file)
@@ -6,7 +6,9 @@
 #include <types/types.h>
 #include <talloc/talloc.h>
 #include <util/util.h>
 #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"
 
 #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]);
                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;
 }
 
        return 0;
 }
 
index cebe787fddb44d5c480461f4f91c38e207e992a5..f7f75e07ceae60212f3ccef5cea34d7ed3ef93aa 100644 (file)
@@ -96,6 +96,12 @@ out_add:
        d_opt->boot_image = create_devpath_resource(d_opt,
                                conf->dc->device, value);
 
        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);
        if (root) {
                opt->boot_args = talloc_asprintf(opt, "root=%s %s", root, args);
                talloc_free(args);
index 4481e5da9f7c9709d31cea7e00e29709cf203d38..4aae8b14bf2eccf27cd209fd8870218e4345d2d6 100644 (file)
@@ -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);
 
                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);
        } else if (streq(name, "INITRD")) {
                url = pxe_url_join(ctx->dc, ctx->dc->conf_url, value);
                opt->initrd = create_url_resource(opt, url);
index 7350b6c3c4ef558927097c53066e81759d0a0a1f..6ea754fea808de474368b887a43278aef8248fbb 100644 (file)
@@ -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,
 }
 
 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;
 {
        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;
        }
 
                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);
 
        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);
 
        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;
        }
        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) {
 
        val = event_get_param(event, "rootpath");
        if (val) {
index aa993928f86c5b6121f6bce95c094f2b8f746501..b62f39db7a9466a3d2da2fdd8698568d6dabedff 100644 (file)
@@ -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);
        /* 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);
        if (state->initrd) {
                state->opt->initrd = create_yaboot_devpath_resource(state,
                                conf, state->initrd);
index 09bc1aa30a61258c068844274563e2a03da89b98..bb7dfe489132c0493389a1826f70e05f83e08850 100644 (file)
@@ -20,6 +20,13 @@ lib_libpbcore_la_CPPFLAGS = \
        $(AM_CPPFLAGS) \
        -DPREFIX='"$(prefix)"'
 
        $(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 \
 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/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 += \
 
 if ENABLE_MTD
 lib_libpbcore_la_SOURCES += \
index 1bde9fb805ba906c95ca4ec24df9d1dc921994af..6a270a301c47df9e2608e55fb9c3bbad2123dba4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
 /*
  *  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
  *
  *  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
 #include <sys/types.h>
 
 #include <talloc/talloc.h>
 #include <sys/types.h>
 
 #include <talloc/talloc.h>
+#include <log/log.h>
 
 #include "file.h"
 
 
 #include "file.h"
 
+#define MAX_FILENAME_SIZE      8192
+#define FILE_XFER_BUFFER_SIZE  8192
+
 static const int max_file_size = 1024 * 1024;
 
 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;
 int read_file(void *ctx, const char *filename, char **bufp, int *lenp)
 {
        struct stat statbuf;
index 8aa7d3c92ced1142b5f1fcc742420c18aa85344d..a2744a03325b7171d29d1c4193a404bee191267c 100644 (file)
@@ -1,5 +1,6 @@
 /*
  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
 /*
  *  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
  *
  *  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
 
 #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);
 
 int read_file(void *ctx, const char *filename, char **bufp, int *lenp);
 int replace_file(const char *filename, char *buf, int len);
 
index 7887fb00cbd19f7bc43216a68ce47a562d3916c8..1560ef797fe1ed31f0e0c98bf9ad215092f44eba 100644 (file)
@@ -37,6 +37,7 @@
  *    4-byte len, initrd_file
  *    4-byte len, dtb_file
  *    4-byte len, boot_args
  *    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:
  *
  * 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, 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\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->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);
 }
 
                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->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);
 }
 
                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->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);
 
        *(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->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);
        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->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);
        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->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;
 
        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 (file)
index 0000000..a377b55
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ *  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"
+
+/*
+ * If --with-signed-boot is enabled lib/security provides the ability to handle
+ * gpg-signed and/or encrypted boot sources (kernel, initrd, etc).
+ * This can be used to enable a form of secure boot, but it is important to
+ * recognise that it depends on the security of the entire system, for example
+ * a full trusted-boot implementation. Petitboot can not and will not be able
+ * to guarantee secure boot by itself.
+ */
+
+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 (file)
index 0000000..fb418bb
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  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 __attribute__((unused)),
+                       struct pb_url *base_file __attribute__((unused)))
+{
+       return NULL;
+}
+
+int verify_file_signature(const char *plaintext_filename __attribute__((unused)),
+       const char *signature_filename __attribute__((unused)),
+       FILE *authorized_signatures_handle __attribute__((unused)),
+       const char *keyring_path __attribute__((unused)))
+{
+       return -1;
+}
+
+int gpg_validate_boot_files(struct boot_task *boot_task __attribute__((unused)))
+{
+       return 0;
+}
+
+void gpg_validate_boot_files_cleanup(struct boot_task *boot_task __attribute__((unused)))
+{}
+
+#endif /* HAVE_LIBGPGME */
+
+#endif /* _PB_GPG_H */
\ No newline at end of file
index 5c5f6ed80110e8e341bc2f3380add6b63f2affa8..6b607cdeecccfd622a5ee3cf0c33a41b52cbe2b4 100644 (file)
@@ -52,6 +52,7 @@ struct boot_option {
        char            *initrd_file;
        char            *dtb_file;
        char            *boot_args;
        char            *initrd_file;
        char            *dtb_file;
        char            *boot_args;
+       char            *args_sig_file;
        bool            is_default;
 
        struct list_item        list;
        bool            is_default;
 
        struct list_item        list;
@@ -65,6 +66,7 @@ struct boot_command {
        char *initrd_file;
        char *dtb_file;
        char *boot_args;
        char *initrd_file;
        char *dtb_file;
        char *boot_args;
+       char *args_sig_file;
        char *tty;
 };
 
        char *tty;
 };
 
diff --git a/m4/gpgme.m4 b/m4/gpgme.m4
new file mode 100644 (file)
index 0000000..6c2be44
--- /dev/null
@@ -0,0 +1,283 @@
+# gpgme.m4 - autoconf macro to detect GPGME.
+# Copyright (C) 2002, 2003, 2004, 2014 g10 Code GmbH
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Last-changed: 2014-10-02
+
+
+AC_DEFUN([_AM_PATH_GPGME_CONFIG],
+[ AC_ARG_WITH(gpgme-prefix,
+            AC_HELP_STRING([--with-gpgme-prefix=PFX],
+                           [prefix where GPGME is installed (optional)]),
+     gpgme_config_prefix="$withval", gpgme_config_prefix="")
+  if test x"${GPGME_CONFIG}" = x ; then
+     if test x"${gpgme_config_prefix}" != x ; then
+        GPGME_CONFIG="${gpgme_config_prefix}/bin/gpgme-config"
+     else
+       case "${SYSROOT}" in
+         /*)
+           if test -x "${SYSROOT}/bin/gpgme-config" ; then
+             GPGME_CONFIG="${SYSROOT}/bin/gpgme-config"
+           fi
+           ;;
+         '')
+           ;;
+          *)
+           AC_MSG_WARN([Ignoring \$SYSROOT as it is not an absolute path.])
+           ;;
+       esac
+     fi
+  fi
+
+  AC_PATH_PROG(GPGME_CONFIG, gpgme-config, no)
+
+  if test "$GPGME_CONFIG" != "no" ; then
+    gpgme_version=`$GPGME_CONFIG --version`
+  fi
+  gpgme_version_major=`echo $gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+  gpgme_version_minor=`echo $gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+  gpgme_version_micro=`echo $gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+])
+
+
+AC_DEFUN([_AM_PATH_GPGME_CONFIG_HOST_CHECK],
+[
+    gpgme_config_host=`$GPGME_CONFIG --host 2>/dev/null || echo none`
+    if test x"$gpgme_config_host" != xnone ; then
+      if test x"$gpgme_config_host" != x"$host" ; then
+  AC_MSG_WARN([[
+***
+*** The config script $GPGME_CONFIG was
+*** built for $gpgme_config_host and thus may not match the
+*** used host $host.
+*** You may want to use the configure option --with-gpgme-prefix
+*** to specify a matching config script or use \$SYSROOT.
+***]])
+        gpg_config_script_warn="$gpg_config_script_warn gpgme"
+      fi
+    fi
+])
+
+
+dnl AM_PATH_GPGME([MINIMUM-VERSION,
+dnl               [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme and define GPGME_CFLAGS and GPGME_LIBS.
+dnl
+dnl If a prefix option is not used, the config script is first
+dnl searched in $SYSROOT/bin and then along $PATH.  If the used
+dnl config script does not match the host specification the script
+dnl is added to the gpg_config_script_warn variable.
+dnl
+AC_DEFUN([AM_PATH_GPGME],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+  tmp=ifelse([$1], ,1:0.4.2,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+     req_gpgme_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+     min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+     req_gpgme_api=0
+     min_gpgme_version="$tmp"
+  fi
+
+  AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version)
+  ok=no
+  if test "$GPGME_CONFIG" != "no" ; then
+    req_major=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+    req_minor=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+    req_micro=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    if test "$gpgme_version_major" -gt "$req_major"; then
+        ok=yes
+    else
+        if test "$gpgme_version_major" -eq "$req_major"; then
+            if test "$gpgme_version_minor" -gt "$req_minor"; then
+               ok=yes
+            else
+               if test "$gpgme_version_minor" -eq "$req_minor"; then
+                   if test "$gpgme_version_micro" -ge "$req_micro"; then
+                     ok=yes
+                   fi
+               fi
+            fi
+        fi
+    fi
+  fi
+  if test $ok = yes; then
+     # If we have a recent GPGME, we should also check that the
+     # API is compatible.
+     if test "$req_gpgme_api" -gt 0 ; then
+        tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+        if test "$tmp" -gt 0 ; then
+           if test "$req_gpgme_api" -ne "$tmp" ; then
+             ok=no
+           fi
+        fi
+     fi
+  fi
+  if test $ok = yes; then
+    GPGME_CFLAGS=`$GPGME_CONFIG --cflags`
+    GPGME_LIBS=`$GPGME_CONFIG --libs`
+    AC_MSG_RESULT(yes)
+    ifelse([$2], , :, [$2])
+    _AM_PATH_GPGME_CONFIG_HOST_CHECK
+  else
+    GPGME_CFLAGS=""
+    GPGME_LIBS=""
+    AC_MSG_RESULT(no)
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GPGME_CFLAGS)
+  AC_SUBST(GPGME_LIBS)
+])
+
+dnl AM_PATH_GPGME_PTHREAD([MINIMUM-VERSION,
+dnl                       [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme and define GPGME_PTHREAD_CFLAGS
+dnl  and GPGME_PTHREAD_LIBS.
+dnl
+AC_DEFUN([AM_PATH_GPGME_PTHREAD],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+  tmp=ifelse([$1], ,1:0.4.2,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+     req_gpgme_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+     min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+     req_gpgme_api=0
+     min_gpgme_version="$tmp"
+  fi
+
+  AC_MSG_CHECKING(for GPGME pthread - version >= $min_gpgme_version)
+  ok=no
+  if test "$GPGME_CONFIG" != "no" ; then
+    if `$GPGME_CONFIG --thread=pthread 2> /dev/null` ; then
+      req_major=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+      req_minor=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+      req_micro=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+      if test "$gpgme_version_major" -gt "$req_major"; then
+        ok=yes
+      else
+        if test "$gpgme_version_major" -eq "$req_major"; then
+          if test "$gpgme_version_minor" -gt "$req_minor"; then
+            ok=yes
+          else
+            if test "$gpgme_version_minor" -eq "$req_minor"; then
+              if test "$gpgme_version_micro" -ge "$req_micro"; then
+                ok=yes
+              fi
+            fi
+          fi
+        fi
+      fi
+    fi
+  fi
+  if test $ok = yes; then
+     # If we have a recent GPGME, we should also check that the
+     # API is compatible.
+     if test "$req_gpgme_api" -gt 0 ; then
+        tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+        if test "$tmp" -gt 0 ; then
+           if test "$req_gpgme_api" -ne "$tmp" ; then
+             ok=no
+           fi
+        fi
+     fi
+  fi
+  if test $ok = yes; then
+    GPGME_PTHREAD_CFLAGS=`$GPGME_CONFIG --thread=pthread --cflags`
+    GPGME_PTHREAD_LIBS=`$GPGME_CONFIG --thread=pthread --libs`
+    AC_MSG_RESULT(yes)
+    ifelse([$2], , :, [$2])
+    _AM_PATH_GPGME_CONFIG_HOST_CHECK
+  else
+    GPGME_PTHREAD_CFLAGS=""
+    GPGME_PTHREAD_LIBS=""
+    AC_MSG_RESULT(no)
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GPGME_PTHREAD_CFLAGS)
+  AC_SUBST(GPGME_PTHREAD_LIBS)
+])
+
+
+dnl AM_PATH_GPGME_GLIB([MINIMUM-VERSION,
+dnl               [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme-glib and define GPGME_GLIB_CFLAGS and GPGME_GLIB_LIBS.
+dnl
+AC_DEFUN([AM_PATH_GPGME_GLIB],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+  tmp=ifelse([$1], ,1:0.4.2,$1)
+  if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+     req_gpgme_api=`echo "$tmp"     | sed 's/\(.*\):\(.*\)/\1/'`
+     min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+  else
+     req_gpgme_api=0
+     min_gpgme_version="$tmp"
+  fi
+
+  AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version)
+  ok=no
+  if test "$GPGME_CONFIG" != "no" ; then
+    req_major=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+    req_minor=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+    req_micro=`echo $min_gpgme_version | \
+               sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    if test "$gpgme_version_major" -gt "$req_major"; then
+        ok=yes
+    else
+        if test "$gpgme_version_major" -eq "$req_major"; then
+            if test "$gpgme_version_minor" -gt "$req_minor"; then
+               ok=yes
+            else
+               if test "$gpgme_version_minor" -eq "$req_minor"; then
+                   if test "$gpgme_version_micro" -ge "$req_micro"; then
+                     ok=yes
+                   fi
+               fi
+            fi
+        fi
+    fi
+  fi
+  if test $ok = yes; then
+     # If we have a recent GPGME, we should also check that the
+     # API is compatible.
+     if test "$req_gpgme_api" -gt 0 ; then
+        tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+        if test "$tmp" -gt 0 ; then
+           if test "$req_gpgme_api" -ne "$tmp" ; then
+             ok=no
+           fi
+        fi
+     fi
+  fi
+  if test $ok = yes; then
+    GPGME_GLIB_CFLAGS=`$GPGME_CONFIG --glib --cflags`
+    GPGME_GLIB_LIBS=`$GPGME_CONFIG --glib --libs`
+    AC_MSG_RESULT(yes)
+    ifelse([$2], , :, [$2])
+    _AM_PATH_GPGME_CONFIG_HOST_CHECK
+  else
+    GPGME_GLIB_CFLAGS=""
+    GPGME_GLIB_LIBS=""
+    AC_MSG_RESULT(no)
+    ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GPGME_GLIB_CFLAGS)
+  AC_SUBST(GPGME_GLIB_LIBS)
+])
index 6247dd0471b6b5fa6f8247adb68f872297eb6c52..5dbd99bee70f19d3141d045b85bd12fcf728db26 100644 (file)
@@ -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->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);
 }
 
        command->tty = ttyname(STDIN_FILENO);
 }
 
index 542a275bbc6230095cd93c1122125f2ae7dca085..59d2df91e33a50bdb048f8f9a268afe14ec73fcb 100644 (file)
@@ -11,6 +11,7 @@ struct pb_boot_data {
        char *initrd;
        char *dtb;
        char *args;
        char *initrd;
        char *dtb;
        char *args;
+       char *args_sig_file;
 };
 
 /**
 };
 
 /**
index 4012ec5a7658dfdc8f429e00130307ed7692ec44..7fa1a42f0eddfbc07794b9f003c4dbfd330dfac6 100644 (file)
@@ -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_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;
                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                    *initrd;
        char                    *dtb;
        char                    *args;
+       char                    *args_sig_file;
+
+       bool                    use_signature_files;
 };
 
 extern const struct help_text boot_editor_help_text;
 };
 
 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;
 
        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;
 }
 
        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);
 
        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,
 
        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 (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;
        /* 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;
                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,
 }
 
 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);
 
        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,
        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,
@@ -547,12 +586,22 @@ struct boot_editor *boot_editor_init(struct cui *cui,
                                struct pb_boot_data *bd))
 {
        struct boot_editor *boot_editor;
                                struct pb_boot_data *bd))
 {
        struct boot_editor *boot_editor;
+       int ncols1, ncols2, ncols3;
 
        boot_editor = talloc_zero(cui, struct boot_editor);
 
        if (!boot_editor)
                return NULL;
 
 
        boot_editor = talloc_zero(cui, struct boot_editor);
 
        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;
        talloc_set_destructor(boot_editor, boot_editor_destructor);
        boot_editor->cui = cui;
        boot_editor->item = item;
@@ -561,11 +610,15 @@ struct boot_editor *boot_editor_init(struct cui *cui,
        boot_editor->need_redraw = false;
        boot_editor->need_update = false;
 
        boot_editor->need_redraw = false;
        boot_editor->need_update = false;
 
-       int ncols1 = strncols(_("Device tree:"));
-       int ncols2 = strncols(_("Boot arguments:"));
+       ncols1 = strncols(_("Device tree:"));
+       ncols2 = strncols(_("Boot arguments:"));
+       if (boot_editor->use_signature_files)
+               ncols3 = strncols(_("Argument signature file:"));
+       else
+               ncols3 = 0;
 
        boot_editor->label_x = 1;
 
        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,
 
        nc_scr_init(&boot_editor->scr, pb_boot_editor_sig, 0,
                        cui, boot_editor_process_key,
@@ -584,10 +637,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;
                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_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(
        }
 
        boot_editor->pad = newpad(
index 0c355ccbe4cee172cb9cfd516171851c9fb83b5b..09b63b053f25638176b1837332b88dab387be110 100644 (file)
@@ -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->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);
 
        /* 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("   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);
 
        /* Re-attach the items array. */
        result = set_menu_items(cui->main->ncm, cui->main->items);