summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--discover/boot.c42
-rw-r--r--discover/boot.h2
-rw-r--r--lib/security/gpg.c292
-rw-r--r--lib/security/gpg.h11
4 files changed, 314 insertions, 33 deletions
diff --git a/discover/boot.c b/discover/boot.c
index c4ddfef..c25627d 100644
--- a/discover/boot.c
+++ b/discover/boot.c
@@ -51,9 +51,14 @@ static int kexec_load(struct boot_task *boot_task)
boot_task->local_image_override = NULL;
if ((result = gpg_validate_boot_files(boot_task))) {
- if (result == KEXEC_LOAD_SIGNATURE_FAILURE) {
+ if (result == KEXEC_LOAD_DECRYPTION_FALURE) {
pb_log("%s: Aborting kexec due to"
- " signature verification failure\n", __func__);
+ " decryption failure\n", __func__);
+ goto abort_kexec;
+ }
+ if (result == KEXEC_LOAD_SIGNATURE_FAILURE) {
+ pb_log("%s: Aborting kexec due to signature"
+ " verification failure\n", __func__);
goto abort_kexec;
}
}
@@ -391,7 +396,13 @@ static void boot_process(struct load_url_result *result, void *data)
load_pending(task->dtb_signature) ||
load_pending(task->cmdline_signature))
return;
+ }
+ if (task->decrypt_files) {
+ if (load_pending(task->cmdline_signature))
+ return;
+ }
+ if (task->verify_signature) {
if (check_load(task, "kernel image signature",
task->image_signature) ||
check_load(task, "initrd signature",
@@ -402,6 +413,14 @@ static void boot_process(struct load_url_result *result, void *data)
task->cmdline_signature))
goto no_sig_load;
}
+ if (task->decrypt_files) {
+ if (load_pending(task->cmdline_signature))
+ return;
+
+ if (check_load(task, "command line signature",
+ task->cmdline_signature))
+ goto no_decrypt_sig_load;
+ }
/* we make a copy of the local paths, as the boot hooks might update
* and/or create these */
@@ -416,6 +435,8 @@ static void boot_process(struct load_url_result *result, void *data)
task->initrd_signature->local : NULL;
task->local_dtb_signature = task->dtb_signature ?
task->dtb_signature->local : NULL;
+ }
+ if (task->verify_signature || task->decrypt_files) {
task->local_cmdline_signature = task->cmdline_signature ?
task->cmdline_signature->local : NULL;
}
@@ -426,7 +447,11 @@ static void boot_process(struct load_url_result *result, void *data)
_("performing kexec_load"));
rc = kexec_load(task);
- if (rc == KEXEC_LOAD_SIGNATURE_FAILURE) {
+ if (rc == KEXEC_LOAD_DECRYPTION_FALURE) {
+ update_status(task->status_fn, task->status_arg,
+ BOOT_STATUS_ERROR, _("decryption failed"));
+ }
+ else if (rc == KEXEC_LOAD_SIGNATURE_FAILURE) {
update_status(task->status_fn, task->status_arg,
BOOT_STATUS_ERROR,
_("signature verification failed"));
@@ -446,6 +471,8 @@ no_sig_load:
cleanup_load(task->image_signature);
cleanup_load(task->initrd_signature);
cleanup_load(task->dtb_signature);
+
+no_decrypt_sig_load:
cleanup_load(task->cmdline_signature);
no_load:
@@ -494,6 +521,7 @@ struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
struct boot_task *boot_task;
const char *boot_desc;
int rc;
+ int lockdown_type;
if (opt && opt->option->name)
boot_desc = opt->option->name;
@@ -533,7 +561,9 @@ struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
boot_task->status_fn = status_fn;
boot_task->status_arg = status_arg;
- boot_task->verify_signature = (lockdown_status() == PB_LOCKDOWN_SIGN);
+ lockdown_type = lockdown_status();
+ boot_task->verify_signature = (lockdown_type == PB_LOCKDOWN_SIGN);
+ boot_task->decrypt_files = (lockdown_type == PB_LOCKDOWN_DECRYPT);
if (cmd && cmd->boot_args) {
boot_task->args = talloc_strdup(boot_task, cmd->boot_args);
@@ -551,7 +581,7 @@ struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
boot_task->boot_tty = config ? config->boot_tty : NULL;
}
- if (boot_task->verify_signature) {
+ if (boot_task->verify_signature || boot_task->decrypt_files) {
if (cmd && cmd->args_sig_file) {
cmdline_sig = pb_url_parse(opt, cmd->args_sig_file);
} else if (opt && opt->args_sig_file) {
@@ -590,7 +620,9 @@ struct boot_task *boot(void *ctx, struct discover_boot_option *opt,
rc |= start_url_load(boot_task, "dtb signature",
dtb_sig, &boot_task->dtb_signature);
}
+ }
+ if (boot_task->verify_signature || boot_task->decrypt_files) {
rc |= start_url_load(boot_task,
"kernel command line signature", cmdline_sig,
&boot_task->cmdline_signature);
diff --git a/discover/boot.h b/discover/boot.h
index 2190495..2d99b7f 100644
--- a/discover/boot.h
+++ b/discover/boot.h
@@ -32,6 +32,7 @@ struct boot_task {
bool dry_run;
bool cancelled;
bool verify_signature;
+ bool decrypt_files;
struct load_url_result *image_signature;
struct load_url_result *initrd_signature;
struct load_url_result *dtb_signature;
@@ -43,6 +44,7 @@ struct boot_task {
};
enum {
+ KEXEC_LOAD_DECRYPTION_FALURE = 252,
KEXEC_LOAD_SIG_SETUP_INVALID = 253,
KEXEC_LOAD_SIGNATURE_FAILURE = 254,
};
diff --git a/lib/security/gpg.c b/lib/security/gpg.c
index a377b55..41d1306 100644
--- a/lib/security/gpg.c
+++ b/lib/security/gpg.c
@@ -60,6 +60,178 @@ struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file)
return signature_file;
}
+int decrypt_file(const char *filename,
+ FILE *authorized_signatures_handle, const char *keyring_path)
+{
+ 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;
+ }
+
+ gpgme_data_release(ciphertext_data);
+
+ 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,
const char *signature_filename, FILE *authorized_signatures_handle,
const char *keyring_path)
@@ -208,10 +380,11 @@ int gpg_validate_boot_files(struct boot_task *boot_task) {
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) ?
+ const char* local_cmdline_signature =
+ (boot_task->verify_signature || boot_task->decrypt_files) ?
boot_task->local_cmdline_signature : NULL;
- if (!boot_task->verify_signature)
+ if ((!boot_task->verify_signature) && (!boot_task->decrypt_files))
return result;
/* Load authorized signatures file */
@@ -283,38 +456,71 @@ int gpg_validate_boot_files(struct boot_task *boot_task) {
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"))
+ if (boot_task->verify_signature) {
+ /* Check signatures */
+ if (verify_file_signature(kernel_filename,
+ local_image_signature,
+ authorized_signatures_handle,
+ "/etc/gpg"))
+ if (verify_file_signature(cmdline_template,
+ local_cmdline_signature,
+ authorized_signatures_handle,
+ "/etc/gpg"))
+
+ 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);
+ /* Clean up */
+ if (cmdline_handle) {
+ fclose(cmdline_handle);
+ unlink(cmdline_template);
+ }
+ fclose(authorized_signatures_handle);
}
- fclose(authorized_signatures_handle);
return result;
}
void gpg_validate_boot_files_cleanup(struct boot_task *boot_task) {
- if (boot_task->verify_signature) {
+ 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);
@@ -330,8 +536,38 @@ void gpg_validate_boot_files_cleanup(struct boot_task *boot_task) {
}
int lockdown_status() {
+ /* assume most restrictive lockdown type */
+ int ret = PB_LOCKDOWN_SIGN;
+
if (access(LOCKDOWN_FILE, F_OK) == -1)
return PB_LOCKDOWN_NONE;
- else
- return PB_LOCKDOWN_SIGN;
+
+ /* determine lockdown type */
+ FILE *authorized_signatures_handle = NULL;
+ authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r");
+ if (!authorized_signatures_handle)
+ return ret;
+
+ 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;
+ }
+ }
+ free(auth_sig_line);
+
+ return ret;
} \ No newline at end of file
diff --git a/lib/security/gpg.h b/lib/security/gpg.h
index fb418bb..6efc3d2 100644
--- a/lib/security/gpg.h
+++ b/lib/security/gpg.h
@@ -23,6 +23,7 @@
enum {
PB_LOCKDOWN_NONE = 0,
PB_LOCKDOWN_SIGN = 1,
+ PB_LOCKDOWN_DECRYPT = 2,
};
#if defined(HAVE_LIBGPGME)
@@ -37,6 +38,9 @@ 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);
@@ -59,6 +63,13 @@ int verify_file_signature(const char *plaintext_filename __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;
OpenPOWER on IntegriCloud