diff options
-rw-r--r-- | lib/Makefile.am | 42 | ||||
-rw-r--r-- | lib/security/common.c | 230 | ||||
-rw-r--r-- | lib/security/gpg.c | 202 | ||||
-rw-r--r-- | lib/security/gpg.h | 83 | ||||
-rw-r--r-- | lib/security/none.c | 61 | ||||
-rw-r--r-- | lib/security/openssl.c | 476 | ||||
-rw-r--r-- | lib/security/security.h | 46 |
7 files changed, 849 insertions, 291 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 8f68202..0088e0b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -18,14 +18,20 @@ noinst_LTLIBRARIES += $(core_lib) lib_libpbcore_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ - -DPREFIX='"$(prefix)"' + -DPREFIX='"$(prefix)"' \ + $(OPENSSL_INCLUDES) -if WITH_GPGME -gpg_int_SOURCES = lib/security/gpg.h \ - lib/security/gpg.c -else -gpg_int_SOURCES = -endif +lib_libpbcore_la_LIBADD = \ + $(GPGME_LIBS) \ + $(OPENSSL_LIBS) + +lib_libpbcore_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(OPENSSL_LDFLAGS) + +lib_libpbcore_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(GPGME_CFLAGS) lib_libpbcore_la_SOURCES = \ lib/ccan/endian/endian.h \ @@ -59,19 +65,27 @@ lib_libpbcore_la_SOURCES = \ lib/util/util.h \ lib/flash/config.h \ lib/flash/flash.h \ - $(gpg_int_SOURCES) + lib/security/security.h if ENABLE_MTD lib_libpbcore_la_SOURCES += \ lib/flash/flash.c -lib_libpbcore_la_CPPFLAGS += \ - $(AM_CPPFLAGS) - -lib_libpbcore_la_LDFLAGS = \ - $(AM_LDFLAGS) \ +lib_libpbcore_la_LDFLAGS += \ $(LIBFLASH_LIBS) +endif +if WITH_GPGME lib_libpbcore_la_SOURCES += \ - lib/flash/flash.c + lib/security/common.c \ + lib/security/gpg.c +else +if WITH_OPENSSL +lib_libpbcore_la_SOURCES += \ + lib/security/common.c \ + lib/security/openssl.c +else +lib_libpbcore_la_SOURCES += \ + lib/security/none.c +endif endif diff --git a/lib/security/common.c b/lib/security/common.c new file mode 100644 index 0000000..df04054 --- /dev/null +++ b/lib/security/common.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2016 Raptor Engineering, LLC + * Copyright (C) 2018 Opengear, Inc + * + * 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 <locale.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 "security.h" + +struct pb_url * 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 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->decrypt_files) ? + boot_task->local_cmdline_signature : NULL; + + if ((!boot_task->verify_signature) && (!boot_task->decrypt_files)) + 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); + } + + if (boot_task->verify_signature) { + /* Check signatures */ + if (verify_file_signature(kernel_filename, + local_image_signature, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_SIGNATURE_FAILURE; + if (verify_file_signature(cmdline_template, + local_cmdline_signature, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_SIGNATURE_FAILURE; + + if (boot_task->local_initrd_signature) + if (verify_file_signature(initrd_filename, + local_initrd_signature, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_SIGNATURE_FAILURE; + if (boot_task->local_dtb_signature) + if (verify_file_signature(dtb_filename, + local_dtb_signature, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_SIGNATURE_FAILURE; + + /* Clean up */ + if (cmdline_handle) { + fclose(cmdline_handle); + unlink(cmdline_template); + } + fclose(authorized_signatures_handle); + } else if (boot_task->decrypt_files) { + /* Decrypt files */ + if (decrypt_file(kernel_filename, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_DECRYPTION_FALURE; + if (verify_file_signature(cmdline_template, + local_cmdline_signature, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_SIGNATURE_FAILURE; + if (boot_task->local_initrd) + if (decrypt_file(initrd_filename, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_DECRYPTION_FALURE; + if (boot_task->local_dtb) + if (decrypt_file(dtb_filename, + authorized_signatures_handle, + KEYRING_PATH)) + result = KEXEC_LOAD_DECRYPTION_FALURE; + + /* Clean up */ + if (cmdline_handle) { + fclose(cmdline_handle); + unlink(cmdline_template); + } + fclose(authorized_signatures_handle); + } + + return result; +} + +void validate_boot_files_cleanup(struct boot_task *boot_task) { + if ((boot_task->verify_signature) || (boot_task->decrypt_files)) { + 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); + } +} + diff --git a/lib/security/gpg.c b/lib/security/gpg.c index 76e2c6c..761d6ce 100644 --- a/lib/security/gpg.c +++ b/lib/security/gpg.c @@ -25,6 +25,7 @@ #include <dirent.h> #include <string.h> #include <fcntl.h> +#include <locale.h> #include <sys/types.h> #include <log/log.h> @@ -34,7 +35,9 @@ #include <util/util.h> #include <i18n/i18n.h> -#include "gpg.h" +#include <gpgme.h> + +#include "security.h" /* * If --with-signed-boot is enabled lib/security provides the ability to handle @@ -45,21 +48,6 @@ * 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 decrypt_file(const char *filename, FILE *authorized_signatures_handle, const char *keyring_path) { @@ -362,181 +350,6 @@ int verify_file_signature(const char *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->decrypt_files) ? - boot_task->local_cmdline_signature : NULL; - - if ((!boot_task->verify_signature) && (!boot_task->decrypt_files)) - 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); - } - - if (boot_task->verify_signature) { - /* 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); - } else if (boot_task->decrypt_files) { - /* Decrypt files */ - if (decrypt_file(kernel_filename, - authorized_signatures_handle, - "/etc/gpg")) - result = KEXEC_LOAD_DECRYPTION_FALURE; - if (verify_file_signature(cmdline_template, - local_cmdline_signature, - authorized_signatures_handle, - "/etc/gpg")) - result = KEXEC_LOAD_SIGNATURE_FAILURE; - if (boot_task->local_initrd) - if (decrypt_file(initrd_filename, - authorized_signatures_handle, - "/etc/gpg")) - result = KEXEC_LOAD_DECRYPTION_FALURE; - if (boot_task->local_dtb) - if (decrypt_file(dtb_filename, - authorized_signatures_handle, - "/etc/gpg")) - result = KEXEC_LOAD_DECRYPTION_FALURE; - - /* 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) || (boot_task->decrypt_files)) { - 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() { /* assume most restrictive lockdown type */ int ret = PB_LOCKDOWN_SIGN; @@ -559,8 +372,8 @@ int lockdown_status() { 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-1] == '\r')) + auth_sig_len--; auth_sig_line[auth_sig_len] = 0; if (strcmp(auth_sig_line, "ENCRYPTED") == 0) { /* first line indicates encrypted files @@ -571,5 +384,6 @@ int lockdown_status() { } free(auth_sig_line); - return ret; + return ret; } + diff --git a/lib/security/gpg.h b/lib/security/gpg.h deleted file mode 100644 index 6efc3d2..0000000 --- a/lib/security/gpg.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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, - PB_LOCKDOWN_DECRYPT = 2, -}; - -#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 decrypt_file(const char * 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 decrypt_file(const char * 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 diff --git a/lib/security/none.c b/lib/security/none.c new file mode 100644 index 0000000..8ef54e1 --- /dev/null +++ b/lib/security/none.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 Raptor Engineering, LLC + * Copyright (C) 2018 Opengear, Inc + * + * 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 <stdio.h> + +#include <url/url.h> + +#include "security.h" + +int lockdown_status(void) { return PB_LOCKDOWN_NONE; } + +struct pb_url * 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 decrypt_file(const char * filename __attribute__((unused)), + FILE * authorized_signatures_handle __attribute__((unused)), + const char * keyring_path __attribute__((unused))) +{ + return -1; +} + +int validate_boot_files(struct boot_task *boot_task __attribute__((unused))) +{ + return 0; +} + +void validate_boot_files_cleanup(struct boot_task *boot_task __attribute__((unused))) +{} + diff --git a/lib/security/openssl.c b/lib/security/openssl.c new file mode 100644 index 0000000..03ea332 --- /dev/null +++ b/lib/security/openssl.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2018 Opengear + * + * 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 <locale.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 <openssl/conf.h> +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/cms.h> +#include <openssl/pkcs12.h> + +#include "security.h" + +static const EVP_MD *s_verify_md = NULL; + +static __attribute__((constructor)) void crypto_init(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + OPENSSL_no_config(); + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + ERR_load_CMS_strings(); +#endif + + s_verify_md = EVP_get_digestbyname(VERIFY_DIGEST); + if (!s_verify_md) + pb_log("Specified OpenSSL digest '%s' not found\n", VERIFY_DIGEST); + +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static __attribute__((destructor)) void crypto_fini(void) +{ + EVP_cleanup(); + ERR_free_strings(); +} +#endif + +static int pb_log_print_errors_cb(const char *str, + size_t len __attribute__((unused)), + void *u __attribute__((unused))) +{ + pb_log(" %s\n", str); + return 0; +} + +static int get_pkcs12(FILE *keyfile, X509 **cert, EVP_PKEY **priv) +{ + PKCS12 *p12 = NULL; + int ok = 0; + + rewind(keyfile); + + p12 = d2i_PKCS12_fp(keyfile, NULL); + if (p12) { + /* + * annoying but NULL and "" are two valid but different + * default passwords + */ + if (!PKCS12_parse(p12, NULL, priv, cert, NULL) && + !PKCS12_parse(p12, "", priv, cert, NULL)) { + pb_log("%s: Error parsing OpenSSL PKCS12:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + } else + ok = 1; + + PKCS12_free(p12); + } + + return ok; +} + +static X509 *get_cert(FILE *keyfile) +{ + EVP_PKEY *priv = NULL; + X509 *cert = NULL; + + if (get_pkcs12(keyfile, &cert, &priv)) { + EVP_PKEY_free(priv); + } else { + rewind(keyfile); + ERR_clear_error(); + cert = PEM_read_X509(keyfile, NULL, NULL, NULL); + } + + return cert; +} + +static STACK_OF(X509) *get_cert_stack(FILE *keyfile) +{ + STACK_OF(X509) *certs = sk_X509_new_null(); + X509 *cert = NULL; + + if (certs) { + cert = get_cert(keyfile); + if (cert) + sk_X509_push(certs, get_cert(keyfile)); + } else { + pb_log("%s: Error allocating OpenSSL X509 stack:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + } + + return certs; +} + + +static EVP_PKEY *get_public_key(FILE *keyfile) +{ + EVP_PKEY *pkey = NULL; + X509 *cert = NULL; + + /* + * walk through supported file types looking for a public key: + * + * 1. PKCS12 + * 2. PEM encoded X509 + * 3. PEM encoded raw public key + * + * someday in the future maybe utilize the keyring_path + * as an input for X509_STORE_load_locations for certificate + * validity checking + */ + + cert = get_cert(keyfile); + if (cert) { + pkey = X509_get_pubkey(cert); + X509_free(cert); + } else { + rewind(keyfile); + ERR_clear_error(); + pkey = PEM_read_PUBKEY(keyfile, NULL, NULL, NULL); + } + + /* handles both cases */ + if (!pkey) { + pb_log("%s: Error loading OpenSSL public key:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + } + + return pkey; +} + +int decrypt_file(const char *filename, + FILE *authorized_signatures_handle, + const char *keyring_path __attribute__((unused))) +{ + BIO *content_bio = NULL, *file_bio = NULL, *out_bio = NULL; + STACK_OF(X509) *certs = NULL; + CMS_ContentInfo *cms = NULL; + EVP_PKEY *priv = NULL; + X509 *cert = NULL; + int nok = -1; + char *outptr; + long outl; + int bytes; + + if (!get_pkcs12(authorized_signatures_handle, &cert, &priv)) { + pb_log("%s: Error opening OpenSSL decrypt authorization file:\n", + __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + file_bio = BIO_new_file(filename, "r"); + if (!file_bio) { + pb_log("%s: Error opening OpenSSL decrypt cipher file '%s':\n", + __func__, filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + out_bio = BIO_new(BIO_s_mem()); + if (!out_bio) { + pb_log("%s: Error allocating OpenSSL decrypt output buffer:\n", + __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + /* right now only support signed-envelope CMS */ + + cms = SMIME_read_CMS(file_bio, &content_bio); + if (!cms) { + pb_log("%s: Error parsing OpenSSL CMS decrypt '%s'\n", + __func__, filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + BIO_free(content_bio); + content_bio = BIO_new(BIO_s_mem()); + if (!content_bio) { + pb_log("%s: Error allocating OpenSSL decrypt content buffer:\n", + __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + if (!CMS_decrypt(cms, priv, cert, NULL, out_bio, 0)) { + pb_log("%s: Error in OpenSSL CMS decrypt '%s'\n", + __func__, filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + certs = sk_X509_new_null(); + if (!certs) { + pb_log("%s: Error allocating OpenSSL X509 stack:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + sk_X509_push(certs, cert); + + CMS_ContentInfo_free(cms); + + cms = SMIME_read_CMS(out_bio, &content_bio); + if (!cms) { + pb_log("%s: Error parsing OpenSSL CMS decrypt verify:\n", + __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + /* this is a mem BIO so failure is 0 or -1 */ + if (BIO_reset(out_bio) < 1) { + pb_log("%s: Error resetting OpenSSL decrypt output buffer:\n", + __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + /* in this mode its attached content */ + if (!CMS_verify(cms, certs, NULL, content_bio, out_bio, + CMS_NO_SIGNER_CERT_VERIFY | CMS_BINARY)) { + pb_log("%s: Failed OpenSSL CMS decrypt verify:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + /* reopen the file so we force a truncation */ + BIO_free(file_bio); + file_bio = BIO_new_file(filename, "w"); + if (!file_bio) { + pb_log("%s: Error opening OpenSSL decrypt output file '%s'\n", + __func__, filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + outl = BIO_get_mem_data(out_bio, &outptr); + + while (outl) { + bytes = BIO_write(file_bio, outptr, outl); + if (bytes > 0) { + outl -= (long)bytes; + outptr += bytes; + + } else if (bytes < 0) { + pb_log("%s: OpenSSL decrypt output write failure on file '%s':\n", + __func__, filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + } + + if (!outl) + nok = 0; + +out: + if (cms) + CMS_ContentInfo_free(cms); + BIO_free(file_bio); + BIO_free(content_bio); + BIO_free(out_bio); + X509_free(cert); + sk_X509_free(certs); + EVP_PKEY_free(priv); + return nok; +} + +int verify_file_signature(const char *plaintext_filename, + const char *signature_filename, + FILE *authorized_signatures_handle, + const char *keyring_path __attribute__((unused))) +{ + BIO *signature_bio = NULL, *plaintext_bio = NULL, *content_bio = NULL; + STACK_OF(X509) *certs = NULL; + CMS_ContentInfo *cms = NULL; + ssize_t bytes_read = -1; + EVP_MD_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + char *sigbuf = NULL; + char rdbuf[8192]; + int nok = -1; + int siglen; + + plaintext_bio = BIO_new_file(plaintext_filename, "r"); + if (!plaintext_bio) { + pb_log("%s: Error opening OpenSSL verify plaintext file '%s'\n", + __func__, plaintext_filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + signature_bio = BIO_new_file(signature_filename, "r"); + if (!signature_bio) { + pb_log("%s: Error opening OpenSSL verify signature file '%s'\n", + __func__, signature_filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + /* first check CMS */ + cms = SMIME_read_CMS(signature_bio, &content_bio); + if (cms) { + certs = get_cert_stack(authorized_signatures_handle); + + /* + * this has to always be detached, which means we always + * ignore content_bio and we have to set the NO_SIGNER_CERT_VERIFY + * until such time we implement the keyring_path as a X509_STORE + */ + + if (!CMS_verify(cms, certs, NULL, plaintext_bio, NULL, + CMS_DETACHED | CMS_NO_SIGNER_CERT_VERIFY | CMS_BINARY)) { + pb_log("%s: Failed OpenSSL CMS verify:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + nok = 0; + + } else { + + /* for explicit dgst mode we need an explicit md defined */ + if (!s_verify_md) + goto out; + + ctx = EVP_MD_CTX_create(); + + if (!ctx) { + pb_log("%s: Error allocating OpenSSL MD ctx:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + pkey = get_public_key(authorized_signatures_handle); + if (!pkey) + goto out; + + if (EVP_DigestVerifyInit(ctx, NULL, s_verify_md, NULL, pkey) < 1) { + pb_log("%s: Error initializing OpenSSL verify:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + + while (bytes_read) { + bytes_read = BIO_read(plaintext_bio, rdbuf, 8192); + if (bytes_read > 0) { + if (EVP_DigestVerifyUpdate(ctx, rdbuf, (size_t)(bytes_read)) < 1) { + pb_log("%s: OpenSSL digest update failure on file '%s':\n", + __func__, plaintext_filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + } else if (bytes_read < 0) { + pb_log("%s: OpenSSL read failure on file '%s':\n", + __func__, plaintext_filename); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + goto out; + } + } + + /* + * can't do signature buffer as an update so have to read in whole file + * would be handy if there was some sort of BIO_read_all but there + * doesn't seem to be so rather than reinvent the wheel close it and + * use the existing support + */ + BIO_free(signature_bio); + signature_bio = NULL; + + if (read_file(NULL, signature_filename, &sigbuf, &siglen)) { + pb_log("%s: Error reading OpenSSL signature file '%s'\n", + __func__, signature_filename); + goto out; + } + + if (EVP_DigestVerifyFinal(ctx, (unsigned char*)sigbuf, siglen)) + nok = 0; + else { + pb_log("%s: Error finalizing OpenSSL verify:\n", __func__); + ERR_print_errors_cb(&pb_log_print_errors_cb, NULL); + } + } + +out: + if (cms) + CMS_ContentInfo_free(cms); + talloc_free(sigbuf); + sk_X509_free(certs); + BIO_free(plaintext_bio); + BIO_free(signature_bio); + BIO_free(content_bio); + EVP_PKEY_free(pkey); + EVP_MD_CTX_destroy(ctx); + return nok; +} + +int lockdown_status(void) +{ + /* + * if it's a PKCS12 then we're in decrypt mode since we have the + * private key, otherwise it's sign mode + * + * someday add in support for runtime determination based on what + * files come back in the async sig file load? + */ + FILE *authorized_signatures_handle = NULL; + int ret = PB_LOCKDOWN_SIGN; + PKCS12 *p12 = NULL; + + if (access(LOCKDOWN_FILE, F_OK) == -1) + return PB_LOCKDOWN_NONE; + + /* determine lockdown type */ + + authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r"); + if (authorized_signatures_handle) { + p12 = d2i_PKCS12_fp(authorized_signatures_handle, NULL); + if (p12) { + ret = PB_LOCKDOWN_DECRYPT; + PKCS12_free(p12); + } + fclose(authorized_signatures_handle); + } + + return ret; +} + diff --git a/lib/security/security.h b/lib/security/security.h new file mode 100644 index 0000000..9b1ae57 --- /dev/null +++ b/lib/security/security.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Raptor Engineering, LLC + * Copyright (C) 2018 Opengear, Inc + * + * 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_SECURITY_H +#define _PB_SECURITY_H + +#include <discover/boot.h> + +enum { + PB_LOCKDOWN_NONE = 0, + PB_LOCKDOWN_SIGN = 1, + PB_LOCKDOWN_DECRYPT = 2 +}; + + +int lockdown_status(void); + +struct pb_url * 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 decrypt_file(const char *filename, + FILE * authorized_signatures_handle, const char * keyring_path); + +int validate_boot_files(struct boot_task *boot_task); + +void validate_boot_files_cleanup(struct boot_task *boot_task); + +#endif // _PB_SECURITY_H + |