#include <dirent.h>
#include <string.h>
#include <fcntl.h>
+#include <locale.h>
#include <sys/types.h>
#include <log/log.h>
#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
* 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_fn("OpenPGP support not available\n");
+ return -1;
+ }
+ err = gpgme_get_engine_info(&enginfo);
+ if (err != GPG_ERR_NO_ERROR) {
+ pb_log_fn("GPG engine failed to initialize\n");
+ return -1;
+ }
+ err = gpgme_new(&gpg_context);
+ if (err != GPG_ERR_NO_ERROR) {
+ pb_log_fn("GPG context could not be created\n");
+ return -1;
+ }
+ err = gpgme_set_protocol(gpg_context, GPGME_PROTOCOL_OpenPGP);
+ if (err != GPG_ERR_NO_ERROR) {
+ pb_log_fn("GPG protocol could not be set\n");
+ 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_fn("Could not set GPG engine information\n");
+ 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_fn("Could not decrypt file\n");
+ 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_fn("Incorrect GPG signature\n");
+ 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,
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__);
+ pb_log_fn("OpenPGP support not available\n");
return -1;
}
err = gpgme_get_engine_info(&enginfo);
if (err != GPG_ERR_NO_ERROR) {
- pb_log("%s: GPG engine failed to initialize\n", __func__);
+ pb_log_fn("GPG engine failed to initialize\n");
return -1;
}
err = gpgme_new(&gpg_context);
if (err != GPG_ERR_NO_ERROR) {
- pb_log("%s: GPG context could not be created\n", __func__);
+ pb_log_fn("GPG context could not be created\n");
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__);
+ pb_log_fn("GPG protocol could not be set\n");
return -1;
}
if (keyring_path)
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__);
+ pb_log_fn("Could not set GPG engine information\n");
return -1;
}
err = gpgme_data_new_from_file(&plaintext_data, plaintext_filename, 1);
gpgme_release(gpg_context);
if (!valid) {
- pb_log("%s: Incorrect GPG signature\n", __func__);
+ pb_log_fn("Incorrect GPG signature\n");
return -1;
}
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;
+ 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;
}
}
- 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;
+ free(auth_sig_line);
- /* Clean up */
- if (cmdline_handle) {
- fclose(cmdline_handle);
- unlink(cmdline_template);
- }
- fclose(authorized_signatures_handle);
-
- return result;
+ return ret;
}
-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