Introducing the following db and dbx commands 1. append_list_db: Show the list of trusted certificates and binary hashes from the db list. 2. append_list_dbx: Show the list of distrusted certificates and binary/certificate hashes from the dbx list. 3. append_add_db_cert: Add the trusted certificate to the db list. 4. append_add_db_hash: Add the trusted binary hash to the db list. 5. append_add_dbx_cert: Add the distrusted certificate to the dbx list. 6. append_add_dbx_hash: Add the distrusted certificate/binary hash to the dbx list.
Note that if signature verification (check_appended_signature) is set to enforce, 1. When append_add_db_cert or append_add_dbx_cert executes, then the certificate file must be signed with an appended signature. 2. When append_add_db_hash executes, then the binary hash file must be signed with an appended signature. 3. When append_add_dbx_hash executes, then the certificate/binary hash file must be signed with an appended signature. Signed-off-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com> Reviewed-by: Avnish Chouhan <avn...@linux.ibm.com> --- grub-core/commands/appendedsig/appendedsig.c | 427 ++++++++++++++++++- include/grub/file.h | 2 + 2 files changed, 419 insertions(+), 10 deletions(-) diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index 146017867..bc2cec977 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -42,6 +42,9 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Public key type. */ #define GRUB_PKEY_ID_PKCS7 2 +#define OPTION_BINARY_HASH 0 +#define OPTION_CERT_HASH 1 + /* Appended signature magic string. */ static const char magic[] = "~Module signature appended~\n"; @@ -102,6 +105,13 @@ static grub_size_t append_sig_len = 0; */ static bool check_sigs = false; +static const struct grub_arg_option options[] = +{ + {"binary-hash", 'b', 0, N_("hash file of the binary."), 0, ARG_TYPE_PATHNAME}, + {"cert-hash", 'c', 1, N_("hash file of the certificate."), 0, ARG_TYPE_PATHNAME}, + {0, 0, 0, 0, 0, 0} +}; + static void register_appended_signatures_cmd (void); static void @@ -191,10 +201,16 @@ grub_env_write_key_mgmt (struct grub_env_var *var __attribute__ ((unused)), cons if (grub_pks_is_support_pks == true && grub_pks_use_keystore == false) build_pks_keystore (); + unregister_appended_signatures_cmd (); grub_pks_use_keystore = true; + register_appended_signatures_cmd (); } else if ((*val == '0') || (*val == 's')) - grub_pks_use_keystore = false; + { + unregister_appended_signatures_cmd (); + grub_pks_use_keystore = false; + register_appended_signatures_cmd (); + } ret = grub_strdup (grub_env_read_key_mgmt (NULL, NULL)); if (ret == NULL) @@ -751,6 +767,52 @@ remove_cert_from_db (const struct x509_certificate *cert) } } +/* + * We cannot use hexdump() to display hash data because it is typically + * displayed in hexadecimal format, along with an ASCII representation of + * the same data. + * Example: sha256 hash data + * 00000000 52 b5 90 49 64 de 22 d7 4e 5f 4f b4 1b 51 9c 34 |R..Id.".N_O..Q.4| + * 00000010 b1 96 21 7c 91 78 a5 0d 20 8c e9 5c 22 54 53 f7 |..!|.x.. ..\"TS.| + * + * An appended signature only required to display the hexadecimal of the hash data + * by separating each byte with ":". So, we introduced a new method dump_data_to_hex + * to display it. + * Example: Sha256 hash data + * 52:b5:90:49:64:de:22:d7:4e:5f:4f:b4:1b:51:9c:34: + * b1:96:21:7c:91:78:a5:0d:20:8c:e9:5c:22:54:53:f7 + */ +static void +dump_data_to_hex (const grub_uint8_t *data, const grub_size_t length) +{ + grub_size_t i, count = 0; + + for (i = 0; i < length - 1; i++) + { + grub_printf ("%02x:", data[i]); + count++; + if (count == 16) + { + grub_printf ("\n\t "); + count = 0; + } + } + + grub_printf ("%02x\n", data[i]); +} + +static bool +is_cert_present_in_dbx (const struct x509_certificate *cert_in) +{ + struct x509_certificate *cert; + + for (cert = dbx.certs; cert; cert = cert->next) + if (is_cert_match (cert, cert_in) == true) + return true; + + return false; +} + static grub_err_t grub_cmd_verify_signature (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { @@ -783,7 +845,8 @@ grub_cmd_verify_signature (grub_command_t cmd __attribute__ ((unused)), int argc } /* - * Add the trusted certificate to the db list if it is not already present. + * Checks the trusted certificate against dbx list if dynamic key management is enabled. + * And add it to the db list if it is not already present. * Note:- When signature verification is enabled, this command only accepts the * trusted certificate that is signed with an appended signature. * The signature is verified by the appendedsig module. If verification succeeds, @@ -823,6 +886,14 @@ grub_cmd_db_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char ** return err; } + /* Only checks the certificate against dbx if dynamic key management is enabled. */ + if (grub_pks_use_keystore == true) + { + if (is_cert_present_in_dbx (cert) == true) + return grub_error (GRUB_ERR_ACCESS_DENIED, + "could not add trusted certificate, as it is present in the dbx list"); + } + if (is_cert_present_in_db (cert) == true) { certificate_release (cert); @@ -842,6 +913,8 @@ grub_cmd_db_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char ** /* * Remove the distrusted certificate from the db list if it is already present. + * And add it to the dbx list if not present when dynamic key management is + * enabled. * Note:- When signature verification is enabled, this command only accepts the * distrusted certificate that is signed with an appended signature. * The signature is verified by the appended sig module. If verification succeeds, @@ -851,7 +924,9 @@ grub_cmd_db_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char ** * without an appended signature and removes it from the db list. * * Also, note that the removal of the distrusted certificate using this command - * does not persist across reboots. + * does not persist across reboots. If static key management is enabled, the + * append_rm_dbx_cert command is only available in the GRUB console. Else the + * append_add_dbx_cert command is available in the GRUB console. */ static grub_err_t grub_cmd_dbx_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) @@ -862,8 +937,10 @@ grub_cmd_dbx_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char * if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, - "a distrusted X.509 certificate file is expected in DER format\n" - "Example:\n\tappend_rm_dbx_cert <X509_CERTIFICATE>\n"); + "a distrusted X.509 certificate file is expected in DER format\n%s", + ((grub_pks_use_keystore == true) ? + "Example:\n\tappend_add_dbx_cert <X509_CERTIFICATE>\n" : + "Example:\n\tappend_rm_dbx_cert <X509_CERTIFICATE>\n")); if (*args == NULL) return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted X.509 certificate file"); @@ -883,8 +960,30 @@ grub_cmd_dbx_cert (grub_command_t cmd __attribute__ ((unused)), int argc, char * /* Remove distrusted certificate from the db list if present. */ remove_cert_from_db (cert); - certificate_release (cert); - grub_free (cert); + + /* Only checks the certificate against dbx if dynamic key management is enabled. */ + if (grub_pks_use_keystore == true) + { + if (is_cert_present_in_dbx (cert) == true) + { + certificate_release (cert); + grub_free (cert); + return grub_error (GRUB_ERR_EXISTS, + "could not add distrusted certificate, as it is present in the dbx list"); + } + + grub_dprintf ("appendedsig", "added distrusted certificate with CN: %s to the dbx list\n", + cert->subject); + + cert->next = dbx.certs; + dbx.certs = cert; + dbx.cert_entries++; + } + else + { + certificate_release (cert); + grub_free (cert); + } return GRUB_ERR_NONE; } @@ -909,9 +1008,281 @@ grub_cmd_list_db (grub_command_t cmd __attribute__ ((unused)), int argc __attrib grub_printf ("\tCN: %s\n\n", cert->subject); } + /* Only list the binary hash if dynamic key management is enabled. */ + if (grub_pks_use_keystore == true) + { + for (i = 0; i < db.signature_entries; i++) + { + if (db.signatures[i] != NULL) + { + grub_printf ("Binary hash %u:\n", i + 1); + grub_printf ("\thash: "); + dump_data_to_hex (db.signatures[i], db.signature_size[i]); + } + } + } + return GRUB_ERR_NONE; } +static grub_err_t +grub_cmd_list_dbx (grub_command_t cmd __attribute__((unused)), + int argc __attribute__((unused)), char **args __attribute__((unused))) +{ + struct x509_certificate *cert; + grub_uint32_t i, cert_num = 1; + + for (cert = dbx.certs; cert != NULL; cert = cert->next, cert_num++) + { + grub_printf ("Certificate %u:\n", cert_num); + grub_printf ("\tserial: "); + + for (i = 0; i < cert->serial_len - 1; i++) + grub_printf ("%02x:", cert->serial[i]); + + grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); + grub_printf ("\tissuer: %s\n", cert->issuer); + grub_printf ("\tCN: %s\n\n", cert->subject); + } + + for (i = 0; i < dbx.signature_entries; i++) + { + if (dbx.signatures[i] != NULL) + { + grub_printf ("Certificate/Binary hash %u:\n", i + 1); + grub_printf ("\thash: "); + dump_data_to_hex (dbx.signatures[i], dbx.signature_size[i]); + } + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +read_hash_from_file (char *file_path, grub_uint8_t **hash_data, grub_size_t *hash_data_size) +{ + grub_err_t rc; + grub_file_t hash_file; + + hash_file = grub_file_open (file_path, GRUB_FILE_TYPE_HASH_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (hash_file == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "unable to open %s file", file_path); + + rc = file_read_whole (hash_file, hash_data, hash_data_size); + grub_file_close (hash_file); + + /* + * If signature verification is enabled and GRUB is locked down, + * obtain the actual hash data size by subtracting the appended + * signature size from the hash data size because + * the hash has an appended signature, and this actual hash data size is + * used to get the hash data. + */ + if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + *hash_data_size -= append_sig_len; + + return rc; +} + +static bool +is_hash_present_in_db (const grub_uint8_t *hash_data, const grub_size_t hash_data_size) +{ + grub_uint32_t i; + + for (i = 0; i < db.signature_entries; i++) + if (grub_memcmp (db.signatures[i], hash_data, hash_data_size) == 0) + return true; + + return false; +} + +static bool +is_hash_present_in_dbx (const grub_uint8_t *hash_data, const grub_size_t hash_data_size) +{ + grub_uint32_t i; + + for (i = 0; i < dbx.signature_entries; i++) + if (grub_memcmp (dbx.signatures[i], hash_data, hash_data_size) == 0) + return true; + + return false; +} + +static void +remove_hash_from_db (const grub_uint8_t *hash_data, const grub_size_t hash_data_size) +{ + grub_uint32_t i; + + for (i = 0; i < db.signature_entries; i++) + { + if (grub_memcmp (db.signatures[i], hash_data, hash_data_size) == 0) + { + grub_dprintf ("appendedsig", "removed distrusted hash %02x%02x%02x%02x.. from the db list\n", + db.signatures[i][0], db.signatures[i][1], db.signatures[i][2], + db.signatures[i][3]); + grub_free (db.signatures[i]); + db.signatures[i] = NULL; + db.signature_size[i] = 0; + break; + } + } +} + +/* + * Remove the trusted binary hash from the dbx list if present. + * And add them to the db list if it is not already present. + * Note:- When signature verification is enabled, this command only accepts + * the binary hash file that is signed with an appended signature. + * The signature is verified by the appendedsig module. If verification succeeds, + * the binary hash is added to the db list. Otherwise, an error is posted and + * the binary hash is not added. + * When signature verification is disabled, it accepts the binary hash file without + * an appended signature and adds it to the db list. + * + * Also, note that the adding of the trusted binary hash using this command does + * not persist across reboots. + */ +static grub_err_t +grub_cmd_db_hash (grub_command_t cmd __attribute__((unused)), int argc, char**args) +{ + grub_err_t rc; + grub_uint8_t *hash_data = NULL; + grub_size_t hash_data_size = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a trusted binary hash file is expected in ASCII text format\n" + "Example:\n\tappend_add_db_hash <BINARY HASH FILE>\n"); + + if (*args == NULL) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing trusted binary hash file"); + + rc = read_hash_from_file (args[0], &hash_data, &hash_data_size); + if (rc == GRUB_ERR_NONE) + { + grub_dprintf ("appendedsig", + "adding a trusted binary hash %02x%02x%02x%02x...\n with size of %" PRIuGRUB_SIZE "\n", + hash_data[0], hash_data[1], hash_data[2], hash_data[3], hash_data_size); + + /* Only accept SHA256, SHA384 and SHA512 binary hash */ + if (hash_data_size != 32 && hash_data_size != 48 && hash_data_size != 64) + { + grub_free (hash_data); + return grub_error (GRUB_ERR_BAD_SIGNATURE, "unacceptable trusted binary hash type"); + } + + if (is_hash_present_in_dbx (hash_data, hash_data_size) == true) + { + grub_free (hash_data); + return grub_error (GRUB_ERR_ACCESS_DENIED, + "could not add trusted binary hash, as it is present in the dbx list"); + } + + if (is_hash_present_in_db (hash_data, hash_data_size) == false) + { + rc = add_hash (hash_data, hash_data_size, &db.signatures, + &db.signature_size, &db.signature_entries); + if (rc != GRUB_ERR_NONE) + rc = grub_error (rc, "adding of trusted binary hash failed"); + else + grub_dprintf ("appendedsig", + "added trusted binary hash %02x%02x%02x%02x...\n to the db list\n", + hash_data[0], hash_data[1], hash_data[2], hash_data[3]); + } + else + rc = grub_error (GRUB_ERR_EXISTS, + "could not add trusted binary hash, as it is present in the db list"); + } + + grub_free (hash_data); + + return rc; +} + +/* + * Remove the distrusted binary/certificate hash from the db list if present. + * And add them to the dbx list if it is not already present. + * Note:- When signature verification is enabled, this command only accepts + * the binary/certificate hash file that is signed with an appended signature. + * The signature is verified by the appendedsig module. If verification succeeds, + * the binary/certificate hash is added to the dbx list. Otherwise, an error is posted and + * the binary/certificate hash is not added. + * When signature verification is disabled, it accepts the binary/certificate hash file without + * an appended signature and adds it to the dbx list. + * + * Also, note that the adding of the distrusted binary/certificate hash using this command does + * not persist across reboots. + */ +static grub_err_t +grub_cmd_dbx_hash (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_err_t rc; + grub_uint8_t *hash_data = NULL; + grub_size_t hash_data_size = 0; + char *file_path; + + if (!ctxt->state[OPTION_BINARY_HASH].set && !ctxt->state[OPTION_CERT_HASH].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a distrusted certificate/binary hash file is expected in ASCII text format\n" + "Example:\n\tappend_add_dbx_hash [option] <FILE>\n" + "option:\n[-b|--binary-hash] FILE [BINARY HASH FILE]\n" + "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]\n"); + + if (ctxt->state[OPTION_BINARY_HASH].arg == NULL && ctxt->state[OPTION_CERT_HASH].arg == NULL) + return grub_error (GRUB_ERR_BAD_FILENAME, "missing distrusted certificate/binary hash file"); + + if (ctxt->state[OPTION_BINARY_HASH].arg != NULL) + file_path = ctxt->state[OPTION_BINARY_HASH].arg; + else + file_path = ctxt->state[OPTION_CERT_HASH].arg; + + rc = read_hash_from_file (file_path, &hash_data, &hash_data_size); + if (rc == GRUB_ERR_NONE) + { + grub_dprintf ("appendedsig", + "adding a distrusted certificate/binary hash %02x%02x%02x%02x...\n" + " with size of %" PRIuGRUB_SIZE "\n", hash_data[0], hash_data[1], + hash_data[2], hash_data[3], hash_data_size); + + if (ctxt->state[OPTION_BINARY_HASH].set || ctxt->state[OPTION_CERT_HASH].set) + { + /* Only accept SHA256, SHA384 and SHA512 certificate/binary hash */ + if (hash_data_size != 32 && hash_data_size != 48 && hash_data_size != 64) + { + rc = grub_error (GRUB_ERR_BAD_SIGNATURE, + "unacceptable distrusted certificate/binary hash type"); + goto clean; + } + } + + /* Remove distrusted hash from the db list if present. */ + remove_hash_from_db (hash_data, hash_data_size); + + if (is_hash_present_in_dbx (hash_data, hash_data_size) == true) + { + rc = grub_error (GRUB_ERR_EXISTS, + "could not add distrusted certificate/binary hash, " + "as it is present in the dbx list"); + goto clean; + } + + rc = add_hash (hash_data, hash_data_size, &dbx.signatures, + &dbx.signature_size, &dbx.signature_entries); + if (rc != GRUB_ERR_NONE) + rc = grub_error (rc, "adding of distrusted binary/certificate hash failed"); + else + grub_dprintf ("appendedsig", + "added distrusted binary/certificate hash %02x%02x%02x%02x...\n to the dbx list\n", + hash_data[0], hash_data[1], hash_data[2], hash_data[3]); + } + + clean: + grub_free (hash_data); + + return rc; +} + static grub_err_t appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type type, void **context __attribute__ ((unused)), enum grub_verify_flags *flags) @@ -932,6 +1303,11 @@ appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type t * verifier, but we lack the hubris required to take this on. Instead, * require that it have an appended signature. */ + case GRUB_FILE_TYPE_HASH_TRUST: + /* + * This is a certificate/binary hash to add to db/dbx. + * This needs to be verified or blocked. + */ case GRUB_FILE_TYPE_LINUX_KERNEL: case GRUB_FILE_TYPE_GRUB_MODULE: /* @@ -985,7 +1361,9 @@ static struct grub_fs pseudo_fs = { .fs_read = pseudo_read }; -static grub_command_t cmd_verify, cmd_list_db, cmd_dbx_cert, cmd_db_cert; +static grub_extcmd_t cmd_dbx_hash; +static grub_command_t cmd_verify, cmd_list_db, cmd_db_cert, cmd_db_hash, + cmd_list_dbx, cmd_dbx_cert; /* Check the certificate hash presence in the PKS dbx list. */ static bool @@ -1287,8 +1665,27 @@ register_appended_signatures_cmd (void) N_("Show the list of trusted X.509 certificates from the db list")); cmd_db_cert = grub_register_command ("append_add_db_cert", grub_cmd_db_cert, N_("X509_CERTIFICATE"), N_("Add trusted X509_CERTIFICATE to the db list")); - cmd_dbx_cert = grub_register_command ("append_rm_dbx_cert", grub_cmd_dbx_cert, N_("X509_CERTIFICATE"), - N_("Remove distrusted X509_CERTIFICATE from the db list")); + /* + * If signature verification is enabled with dynamic key management mode, + * register dynamic secure boot GRUB commands. + */ + if (grub_pks_use_keystore == true) + { + cmd_dbx_cert = grub_register_command ("append_add_dbx_cert", grub_cmd_dbx_cert, N_("X509_CERTIFICATE"), + N_("Add distrusted X509_CERTIFICATE to the dbx list")); + cmd_list_dbx = grub_register_command ("append_list_dbx", grub_cmd_list_dbx, 0, + N_("Show the list of distrusted certificates and" + " certificate/binary hashes from the dbx list")); + cmd_db_hash = grub_register_command ("append_add_db_hash", grub_cmd_db_hash, N_("BINARY HASH FILE"), + N_("Add trusted BINARY HASH to the db list.")); + cmd_dbx_hash = grub_register_extcmd ("append_add_dbx_hash", grub_cmd_dbx_hash, 0, + N_("[-b|--binary-hash] FILE [BINARY HASH FILE]\n" + "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]"), + N_("Add distrusted CERTFICATE/BINARY HASH to the dbx list."), options); + } + else + cmd_dbx_cert = grub_register_command ("append_rm_dbx_cert", grub_cmd_dbx_cert, N_("X509_CERTIFICATE"), + N_("Remove distrusted X509_CERTIFICATE from the db list")); } /* It unregisters the appended signatures GRUB commands. */ @@ -1299,6 +1696,16 @@ unregister_appended_signatures_cmd (void) grub_unregister_command (cmd_list_db); grub_unregister_command (cmd_db_cert); grub_unregister_command (cmd_dbx_cert); + /* + * If signature verification is enabled with dynamic key management mode, + * unregister dynamic secure boot GRUB commands. + */ + if (grub_pks_use_keystore == true) + { + grub_unregister_command (cmd_list_dbx); + grub_unregister_command (cmd_db_hash); + grub_unregister_extcmd (cmd_dbx_hash); + } } GRUB_MOD_INIT (appendedsig) diff --git a/include/grub/file.h b/include/grub/file.h index d678de063..16a4b7d26 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -115,6 +115,8 @@ enum grub_file_type GRUB_FILE_TYPE_HASHLIST, /* File hashed by hashsum. */ GRUB_FILE_TYPE_TO_HASH, + /* File holding certificiate/binary hash to add to db/dbx. */ + GRUB_FILE_TYPE_HASH_TRUST, /* Keyboard layout. */ GRUB_FILE_TYPE_KEYBOARD_LAYOUT, /* Picture file. */ -- 2.39.5 (Apple Git-154) _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel