X-Git-Url: https://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=lib%2Fsecurity%2Fgpg.c;h=aae85aa06e1084fbf365f537b8003bfee3b098b9;hp=a377b5537abf03f74635e4f110adc280908b8f99;hb=18a47a31b46d916c58a31e8784a7c3a3abcae446;hpb=86c9d34380b0074dab1ba89a569a94280d6999c4 diff --git a/lib/security/gpg.c b/lib/security/gpg.c index a377b55..aae85aa 100644 --- a/lib/security/gpg.c +++ b/lib/security/gpg.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,9 @@ #include #include -#include "gpg.h" +#include + +#include "security.h" /* * If --with-signed-boot is enabled lib/security provides the ability to handle @@ -45,19 +48,176 @@ * to guarantee secure boot by itself. */ -struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file) +int decrypt_file(const char *filename, + FILE *authorized_signatures_handle, const char *keyring_path) { - struct pb_url *signature_file = NULL; + int result = 0; + int valid = 0; + size_t bytes_read = 0; + unsigned char buffer[8192]; + + if (filename == NULL) + return -1; + + gpgme_signature_t verification_signatures; + gpgme_verify_result_t verification_result; + gpgme_data_t ciphertext_data; + gpgme_data_t plaintext_data; + gpgme_engine_info_t enginfo; + gpgme_ctx_t gpg_context; + gpgme_error_t err; + + /* 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(&plaintext_data); + if (err != GPG_ERR_NO_ERROR) { + pb_log("%s: Could not create GPG plaintext data buffer\n", + __func__); + return -1; + } + err = gpgme_data_new_from_file(&ciphertext_data, filename, 1); + if (err != GPG_ERR_NO_ERROR) { + pb_log("%s: Could not create GPG ciphertext data buffer" + " from file '%s'\n", __func__, filename); + return -1; + } + + /* Decrypt and verify file */ + err = gpgme_op_decrypt_verify(gpg_context, ciphertext_data, + plaintext_data); + if (err != GPG_ERR_NO_ERROR) { + pb_log("%s: Could not decrypt file\n", __func__); + 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) { + pb_log("%s: Good signature for key ID '%s' ('%s')\n", + __func__, + verification_signatures->fpr, 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); + } + else { + pb_log("%s: Signature for key ID '%s' ('%s') invalid." + " Status: %08x\n", __func__, + verification_signatures->fpr, filename, + verification_signatures->status); + } + verification_signatures = verification_signatures->next; + } - 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); + gpgme_data_release(ciphertext_data); - return signature_file; + if (valid) { + /* Write decrypted file over ciphertext */ + FILE *plaintext_file_handle = NULL; + plaintext_file_handle = fopen(filename, "wb"); + if (!plaintext_file_handle) { + pb_log("%s: Could not create GPG plaintext file '%s'\n", + __func__, filename); + gpgme_data_release(plaintext_data); + gpgme_release(gpg_context); + return -1; + } + gpgme_data_seek(plaintext_data, 0, SEEK_SET); + if (err != GPG_ERR_NO_ERROR) { + pb_log("%s: Could not seek in GPG plaintext buffer\n", + __func__); + gpgme_data_release(plaintext_data); + gpgme_release(gpg_context); + fclose(plaintext_file_handle); + return -1; + } + while ((bytes_read = gpgme_data_read(plaintext_data, buffer, + 8192)) > 0) { + size_t l2 = fwrite(buffer, 1, bytes_read, + plaintext_file_handle); + if (l2 < bytes_read) { + if (ferror(plaintext_file_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; + } + } + fclose(plaintext_file_handle); + } + + /* Clean up */ + gpgme_data_release(plaintext_data); + gpgme_release(gpg_context); + + if (!valid) { + pb_log("%s: Incorrect GPG signature\n", __func__); + return -1; + } + + pb_log("%s: GPG signature for decrypted file '%s' verified\n", + __func__, filename); + + return result; } int verify_file_signature(const char *plaintext_filename, @@ -190,148 +350,42 @@ 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->local_cmdline_signature : NULL; +int lockdown_status() { + /* assume most restrictive lockdown type */ + int ret = PB_LOCKDOWN_SIGN; - if (!boot_task->verify_signature) - return result; +#if !defined(HARD_LOCKDOWN) + if (access(LOCKDOWN_FILE, F_OK) == -1) + return PB_LOCKDOWN_NONE; +#endif - /* Load authorized signatures file */ + /* determine lockdown type */ + FILE *authorized_signatures_handle = NULL; 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; - } + if (!authorized_signatures_handle) + return ret; - /* 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; + char *auth_sig_line = NULL; + size_t auth_sig_len = 0; + ssize_t auth_sig_read; + rewind(authorized_signatures_handle); + if ((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, "ENCRYPTED") == 0) { + /* first line indicates encrypted files + * expected. enable decryption. + */ + ret = PB_LOCKDOWN_DECRYPT; } } - 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; -} + free(auth_sig_line); -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); - } + return ret; } -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