From 65c8d2eb32beda5e90af891de3e5bda41a8aa6f1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Mon, 21 Oct 2013 17:49:33 +0800 Subject: [PATCH 01/18] Update TODO --- TODO | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/TODO b/TODO index 373e48d..465835a 100644 --- a/TODO +++ b/TODO @@ -1,17 +1,5 @@ -* Validate the DER file [DONE] * Show the detail of the DER file when enrolling (?) * Add an option to slience the output (?) -* Show the current value of MokNew and MokListRT [DONE] -* Request the user to input a password before enrolling [DONE] -* Read the list from a volatile RT variable [DONE] -* Export keys in MokListRT [DONE] * List hashes in MokListRT -* Add a new command to probe the state of SecureBoot - - SecureBoot, EFI_GLOBAL_VARIABLE -* Do not to enroll duplicate key - - compare the keys in MokListRT before enrolling the new keys - - compare the keys in MokNew - - add a new command to check duplicate -* Add a new command to append the new key request to the old one - - use the old password to verify the old request -* Delete a specific key +* Support MokX +* Import hash into MokNew, NokDel, MokXNew -- 1.8.1.4 From 012d82be0468e876a10691fbabab2ed11b7a4954 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 22 Oct 2013 14:24:13 +0800 Subject: [PATCH 02/18] Show the hashes in the database --- src/efi.h | 2 +- src/mokutil.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++----- src/signature.h | 14 ++++++-- 3 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/efi.h b/src/efi.h index a622a2b..33579c5 100644 --- a/src/efi.h +++ b/src/efi.h @@ -102,7 +102,7 @@ EFI_GUID( 0x47c7b226, 0xc42a, 0x11d2, 0x8e, 0x57, 0x00, 0xa0, 0xc9, 0x69, 0x72, EFI_GUID( 0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) static inline int -efi_guidcmp(efi_guid_t left, efi_guid_t right) +efi_guidcmp(const efi_guid_t left, const efi_guid_t right) { return memcmp(&left, &right, sizeof (efi_guid_t)); } diff --git a/src/mokutil.c b/src/mokutil.c index e4e247c..62690ef 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -157,14 +157,29 @@ test_and_delete_var (const char *var_name) return 0; } +static uint32_t +signature_size (efi_guid_t hash_type) +{ + if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) + return (SHA_DIGEST_LENGTH + sizeof(efi_guid_t)); + else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) + return (SHA224_DIGEST_LENGTH + sizeof(efi_guid_t)); + else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) + return (SHA256_DIGEST_LENGTH + sizeof(efi_guid_t)); + else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) + return (SHA384_DIGEST_LENGTH + sizeof(efi_guid_t)); + else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) + return (SHA512_DIGEST_LENGTH + sizeof(efi_guid_t)); + + return 0; +} + static MokListNode* build_mok_list (void *data, unsigned long data_size, uint32_t *mok_num) { MokListNode *list; EFI_SIGNATURE_LIST *CertList = data; EFI_SIGNATURE_DATA *Cert; - efi_guid_t CertType = EfiCertX509Guid; - efi_guid_t HashType = EfiHashSha256Guid; unsigned long dbsize = data_size; unsigned long count = 0; @@ -176,16 +191,20 @@ build_mok_list (void *data, unsigned long data_size, uint32_t *mok_num) } while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - if ((efi_guidcmp (CertList->SignatureType, CertType) != 0) && - (efi_guidcmp (CertList->SignatureType, HashType) != 0)) { + if ((efi_guidcmp (CertList->SignatureType, EfiCertX509Guid) != 0) && + (efi_guidcmp (CertList->SignatureType, EfiHashSha1Guid) != 0) && + (efi_guidcmp (CertList->SignatureType, EfiHashSha224Guid) != 0) && + (efi_guidcmp (CertList->SignatureType, EfiHashSha256Guid) != 0) && + (efi_guidcmp (CertList->SignatureType, EfiHashSha384Guid) != 0) && + (efi_guidcmp (CertList->SignatureType, EfiHashSha512Guid) != 0)) { dbsize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *)((uint8_t *) CertList + CertList->SignatureListSize); continue; } - if ((efi_guidcmp (CertList->SignatureType, HashType) == 0) && - (CertList->SignatureSize != 48)) { + if ((efi_guidcmp (CertList->SignatureType, EfiCertX509Guid) != 0) && + (CertList->SignatureSize != signature_size (CertList->SignatureType))) { dbsize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *)((uint8_t *) CertList + CertList->SignatureListSize); @@ -203,8 +222,18 @@ build_mok_list (void *data, unsigned long data_size, uint32_t *mok_num) } list[count].header = CertList; - list[count].mok_size = CertList->SignatureSize - sizeof(efi_guid_t); - list[count].mok = (void *)Cert->SignatureData; + if (efi_guidcmp (CertList->SignatureType, EfiCertX509Guid) == 0) { + /* X509 certificate */ + list[count].mok_size = CertList->SignatureSize - + sizeof(efi_guid_t); + list[count].mok = (void *)Cert->SignatureData; + } else { + /* hash array */ + list[count].mok_size = CertList->SignatureListSize - + sizeof(EFI_SIGNATURE_LIST) - + CertList->SignatureHeaderSize; + list[count].mok = (void *)Cert; + } count++; dbsize -= CertList->SignatureListSize; @@ -258,6 +287,64 @@ print_x509 (char *cert, int cert_size) } static int +print_hash_array (efi_guid_t hash_type, void *hash_array, uint32_t array_size) +{ + uint32_t hash_size, remain; + uint32_t sig_size; + uint8_t *hash; + const char *name; + int i; + + if (!hash_array || array_size == 0) { + fprintf (stderr, "invalid hash array\n"); + return -1; + } + + if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) { + name = "SHA1"; + hash_size = SHA_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) { + name = "SHA224"; + hash_size = SHA224_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) { + name = "SHA256"; + hash_size = SHA256_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) { + name = "SHA384"; + hash_size = SHA384_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) { + name = "SHA512"; + hash_size = SHA512_DIGEST_LENGTH; + } else { + fprintf (stderr, "unknown hash type\n"); + return -1; + } + sig_size = hash_size + sizeof(efi_guid_t); + + printf (" [%s]\n", name); + + remain = array_size; + hash = (uint8_t *)hash_array; + + while (remain > 0) { + if (remain < sig_size) { + fprintf (stderr, "invalid array size\n"); + return -1; + } + + printf (" "); + hash += sizeof(efi_guid_t); + for (i = 0; iSignatureType, EfiCertX509Guid) == 0) { + print_x509 ((char *)list[i].mok, list[i].mok_size); + } else { + print_hash_array (list[i].header->SignatureType, + list[i].mok, list[i].mok_size); + } if (i < mok_num - 1) printf ("\n"); } diff --git a/src/signature.h b/src/signature.h index df88e98..02f0211 100644 --- a/src/signature.h +++ b/src/signature.h @@ -28,11 +28,17 @@ * version. If you delete this exception statement from all source * files in the program, then also delete it here. */ -#define SHA256_DIGEST_SIZE 32 +#ifndef SIGNATURE_H +#define SIGNATURE_H -#define EfiHashSha1Guid EFI_GUID (0x826ca512, 0xcf10, 0x4ac9, 0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd) +#include "efi.h" + +#define EfiHashSha1Guid EFI_GUID (0x826ca512, 0xcf10, 0x4ac9, 0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd) +#define EfiHashSha224Guid EFI_GUID (0xb6e5233, 0xa65c, 0x44c9, 0x94, 0x7, 0xd9, 0xab, 0x83, 0xbf, 0xc8, 0xbd) #define EfiHashSha256Guid EFI_GUID (0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) -#define EfiCertX509Guid EFI_GUID (0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) +#define EfiHashSha384Guid EFI_GUID (0xff3e5307, 0x9fd0, 0x48c9, 0x85, 0xf1, 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1) +#define EfiHashSha512Guid EFI_GUID (0x93e0fae, 0xa6c4, 0x4f50, 0x9f, 0x1b, 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a) +#define EfiCertX509Guid EFI_GUID (0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) typedef struct { /// @@ -71,3 +77,5 @@ typedef struct { /// EFI_SIGNATURE_DATA Signatures[][SignatureSize]; /// } __attribute__ ((packed)) EFI_SIGNATURE_LIST; + +#endif /* SIGNATURE_H */ -- 1.8.1.4 From 77a215f86139b21fd55dca4d032b7269f62b51c1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 22 Oct 2013 14:34:21 +0800 Subject: [PATCH 03/18] Don't allocate the MOK list until there is a node --- src/mokutil.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index 62690ef..ec476dd 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -177,19 +177,12 @@ signature_size (efi_guid_t hash_type) static MokListNode* build_mok_list (void *data, unsigned long data_size, uint32_t *mok_num) { - MokListNode *list; + MokListNode *list = NULL; EFI_SIGNATURE_LIST *CertList = data; EFI_SIGNATURE_DATA *Cert; unsigned long dbsize = data_size; unsigned long count = 0; - list = malloc(sizeof(MokListNode)); - - if (!list) { - fprintf(stderr, "Unable to allocate MOK list\n"); - return NULL; - } - while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { if ((efi_guidcmp (CertList->SignatureType, EfiCertX509Guid) != 0) && (efi_guidcmp (CertList->SignatureType, EfiHashSha1Guid) != 0) && -- 1.8.1.4 From befae0e92ea24e35208b07786857d195ce8aa086 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 22 Oct 2013 14:40:58 +0800 Subject: [PATCH 04/18] Skip hashes while exporting MokListRT --- src/mokutil.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mokutil.c b/src/mokutil.c index ec476dd..04f7655 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1119,6 +1119,10 @@ export_moks () /* mode 644 */ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; for (i = 0; i < mok_num; i++) { + if (efi_guidcmp (list[i].header->SignatureType, EfiCertX509Guid) != 0) + continue; + + /* Dump X509 certificate to files */ snprintf (filename, PATH_MAX, "MOK-%04d.der", i+1); fd = open (filename, O_CREAT | O_WRONLY, mode); if (fd == -1) { -- 1.8.1.4 From 9cfc5f93e15e05dabf46a86e4f8e899e32443176 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 22 Oct 2013 16:05:33 +0800 Subject: [PATCH 05/18] Match the hashes in the database --- src/mokutil.c | 162 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 53 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index 04f7655..fa5d668 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -157,19 +157,50 @@ test_and_delete_var (const char *var_name) return 0; } +static const char* +efi_hash_to_string (efi_guid_t hash_type) +{ + if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) { + return "SHA1"; + } else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) { + return "SHA224"; + } else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) { + return "SHA256"; + } else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) { + return "SHA384"; + } else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) { + return "SHA512"; + } + + return NULL; +} + +static uint32_t +efi_hash_size (efi_guid_t hash_type) +{ + if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) { + return SHA_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) { + return SHA224_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) { + return SHA256_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) { + return SHA384_DIGEST_LENGTH; + } else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) { + return SHA512_DIGEST_LENGTH; + } + + return 0; +} + static uint32_t signature_size (efi_guid_t hash_type) { - if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) - return (SHA_DIGEST_LENGTH + sizeof(efi_guid_t)); - else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) - return (SHA224_DIGEST_LENGTH + sizeof(efi_guid_t)); - else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) - return (SHA256_DIGEST_LENGTH + sizeof(efi_guid_t)); - else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) - return (SHA384_DIGEST_LENGTH + sizeof(efi_guid_t)); - else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) - return (SHA512_DIGEST_LENGTH + sizeof(efi_guid_t)); + uint32_t hash_size; + + hash_size = efi_hash_size (hash_type); + if (hash_size) + return (hash_size + sizeof(efi_guid_t)); return 0; } @@ -293,29 +324,16 @@ print_hash_array (efi_guid_t hash_type, void *hash_array, uint32_t array_size) return -1; } - if (efi_guidcmp (hash_type, EfiHashSha1Guid) == 0) { - name = "SHA1"; - hash_size = SHA_DIGEST_LENGTH; - } else if (efi_guidcmp (hash_type, EfiHashSha224Guid) == 0) { - name = "SHA224"; - hash_size = SHA224_DIGEST_LENGTH; - } else if (efi_guidcmp (hash_type, EfiHashSha256Guid) == 0) { - name = "SHA256"; - hash_size = SHA256_DIGEST_LENGTH; - } else if (efi_guidcmp (hash_type, EfiHashSha384Guid) == 0) { - name = "SHA384"; - hash_size = SHA384_DIGEST_LENGTH; - } else if (efi_guidcmp (hash_type, EfiHashSha512Guid) == 0) { - name = "SHA512"; - hash_size = SHA512_DIGEST_LENGTH; - } else { + name = efi_hash_to_string (hash_type); + hash_size = efi_hash_size (hash_type); + sig_size = hash_size + sizeof(efi_guid_t); + + if (!name) { fprintf (stderr, "unknown hash type\n"); return -1; } - sig_size = hash_size + sizeof(efi_guid_t); printf (" [%s]\n", name); - remain = array_size; hash = (uint8_t *)hash_array; @@ -829,36 +847,78 @@ is_valid_cert (void *cert, uint32_t cert_size) } static int -is_duplicate (const void *cert, const uint32_t cert_size, const char *db_name, - efi_guid_t guid) +match_hash_array (efi_guid_t hash_type, const void *hash, + const void *hash_array, const uint32_t array_size) +{ + uint32_t hash_size, hash_count; + uint32_t sig_size; + int i; + void *ptr; + + hash_size = efi_hash_size (hash_type); + if (!hash_size) + return 0; + + sig_size = hash_size + sizeof(efi_guid_t); + if ((array_size % sig_size) != 0) { + fprintf (stderr, "invalid hash array size\n"); + return 0; + } + + ptr = (void *)hash_array; + hash_count = array_size / sig_size; + for (i = 0; i < hash_count; i++) { + ptr += sizeof(efi_guid_t); + if (memcmp (ptr, hash, hash_size) == 0) + return 1; + ptr += hash_size; + } + + return 0; +} + +static int +is_duplicate (efi_guid_t type, const void *data, const uint32_t data_size, + efi_guid_t vendor, const char *db_name) { efi_variable_t var; - uint32_t mok_num; + uint32_t node_num; MokListNode *list; int i, ret = 0; - if (!cert || cert_size == 0 || !db_name) + if (!data || data_size == 0 || !db_name) return 0; memset (&var, 0, sizeof(var)); var.VariableName = db_name; - var.VendorGuid = guid; + var.VendorGuid = vendor; if (read_variable (&var) != EFI_SUCCESS) return 0; - list = build_mok_list (var.Data, var.DataSize, &mok_num); + list = build_mok_list (var.Data, var.DataSize, &node_num); if (list == NULL) { goto done; } - for (i = 0; i < mok_num; i++) { - if (list[i].mok_size != cert_size) + for (i = 0; i < node_num; i++) { + if (efi_guidcmp (list[i].header->SignatureType, type) != 0) continue; - if (memcmp (list[i].mok, cert, cert_size) == 0) { - ret = 1; - break; + if (efi_guidcmp (type, EfiCertX509Guid) == 0) { + if (list[i].mok_size != data_size) + continue; + + if (memcmp (list[i].mok, data, data_size) == 0) { + ret = 1; + break; + } + } else { + if (match_hash_array (type, data, list[i].mok, + list[i].mok_size)) { + ret = 1; + break; + } } } @@ -870,19 +930,19 @@ done: } static int -is_valid_request (void *mok, uint32_t mok_size, uint8_t import) +is_valid_request (efi_guid_t type, void *mok, uint32_t mok_size, uint8_t import) { if (import) { - if (is_duplicate (mok, mok_size, "PK", EFI_GLOBAL_VARIABLE) || - is_duplicate (mok, mok_size, "KEK", EFI_GLOBAL_VARIABLE) || - is_duplicate (mok, mok_size, "db", EFI_IMAGE_SECURITY_DATABASE_GUID) || - is_duplicate (mok, mok_size, "MokListRT", SHIM_LOCK_GUID) || - is_duplicate (mok, mok_size, "MokNew", SHIM_LOCK_GUID)) { + if (is_duplicate (type, mok, mok_size, EFI_GLOBAL_VARIABLE, "PK") || + is_duplicate (type, mok, mok_size, EFI_GLOBAL_VARIABLE, "KEK") || + is_duplicate (type, mok, mok_size, EFI_IMAGE_SECURITY_DATABASE_GUID, "db") || + is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokListRT") || + is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokNew")) { return 0; } } else { - if (!is_duplicate (mok, mok_size, "MokListRT", SHIM_LOCK_GUID) || - is_duplicate (mok, mok_size, "MokDel", SHIM_LOCK_GUID)) { + if (!is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokListRT") || + is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokDel")) { return 0; } } @@ -1008,7 +1068,7 @@ issue_mok_request (char **files, uint32_t total, uint8_t import, files[i]); } - if (is_valid_request (ptr, sizes[i], import)) { + if (is_valid_request (EfiCertX509Guid, ptr, sizes[i], import)) { ptr += sizes[i]; real_size += sizes[i] + sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); } else if (in_pending_request (ptr, sizes[i], import)) { @@ -1359,11 +1419,7 @@ test_key (const char *key_file) goto error; } - if (!is_duplicate (key, read_size, "PK", EFI_GLOBAL_VARIABLE) && - !is_duplicate (key, read_size, "KEK", EFI_GLOBAL_VARIABLE) && - !is_duplicate (key, read_size, "db", EFI_GLOBAL_VARIABLE) && - !is_duplicate (key, read_size, "MokListRT", SHIM_LOCK_GUID) && - !is_duplicate (key, read_size, "MokNew", SHIM_LOCK_GUID)) { + if (!is_valid_request (EfiCertX509Guid, key, read_size, 1)) { printf ("%s is not enrolled\n", key_file); ret = 0; } else { -- 1.8.1.4 From 9ec6f6836a386d527cf62d6583c3ea5e394f62a5 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 22 Oct 2013 18:01:11 +0800 Subject: [PATCH 06/18] Support MOK blacklist --- src/mokutil.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 195 insertions(+), 28 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index fa5d668..f10e6e8 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -79,6 +79,7 @@ EFI_GUID (0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, #define SIMPLE_HASH (1 << 17) #define IGNORE_DB (1 << 18) #define USE_DB (1 << 19) +#define MOKX (1 << 20) #define DEFAULT_CRYPT_METHOD SHA512_BASED #define DEFAULT_SALT_SIZE SHA512_SALT_MAX @@ -86,6 +87,13 @@ EFI_GUID (0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, static int use_simple_hash; +typedef enum { + DELETE_MOK = 0, + ENROLL_MOK, + DELETE_BLACKLIST, + ENROLL_BLACKLIST +} MokRequest; + typedef struct { EFI_SIGNATURE_LIST *header; uint32_t mok_size; @@ -135,6 +143,10 @@ print_help () printf (" --simple-hash\t\t\t\tUse the old password hash method\n"); printf (" \t\t\t\t(For --import, --delete, --password,\n"); printf (" \t\t\t\t --clear-password and --reset)\n"); + printf (" --mokx\t\t\t\tManipulate the MOK blacklist\n"); + printf (" \t\t\t\t(For --list-enrolled, --list-new,\n"); + printf (" \t\t\t\t --list-delete, --import, --delete,\n"); + printf (" \t\t\t\t --revoke-import, and --revoke-delete)\n"); } static int @@ -724,7 +736,7 @@ get_password_from_shadow (pw_crypt_t *pw_crypt) } static int -update_request (void *new_list, int list_len, uint8_t import, +update_request (void *new_list, int list_len, MokRequest req, const char *hash_file, const int root_pw) { efi_variable_t var; @@ -739,12 +751,25 @@ update_request (void *new_list, int list_len, uint8_t import, bzero (&pw_crypt, sizeof(pw_crypt_t)); pw_crypt.method = DEFAULT_CRYPT_METHOD; - if (import) { + switch (req) { + case ENROLL_MOK: req_name = "MokNew"; auth_name = "MokAuth"; - } else { + break; + case DELETE_MOK: req_name = "MokDel"; auth_name = "MokDelAuth"; + break; + case ENROLL_BLACKLIST: + req_name = "MokXNew"; + auth_name = "MokXAuth"; + break; + case DELETE_BLACKLIST: + req_name = "MokXDel"; + auth_name = "MokXDelAuth"; + break; + default: + return -1; } if (hash_file) { @@ -776,7 +801,7 @@ update_request (void *new_list, int list_len, uint8_t import, } if (new_list) { - /* Write MokNew*/ + /* Write MokNew, MokDel, MokXNew, or MokXDel*/ var.Data = new_list; var.DataSize = list_len; var.VariableName = req_name; @@ -787,15 +812,27 @@ update_request (void *new_list, int list_len, uint8_t import, | EFI_VARIABLE_RUNTIME_ACCESS; if (edit_variable (&var) != EFI_SUCCESS) { - fprintf (stderr, "Failed to %s keys\n", - import ? "enroll new" : "delete"); + switch (req) { + case ENROLL_MOK: + fprintf (stderr, "Failed to enroll new keys\n"); + break; + case ENROLL_BLACKLIST: + fprintf (stderr, "Failed to enroll blacklist\n"); + break; + case DELETE_MOK: + fprintf (stderr, "Failed to delete keys\n"); + break; + case DELETE_BLACKLIST: + fprintf (stderr, "Failed to delete blacklist\n"); + break; + } goto error; } } else { test_and_delete_var (req_name); } - /* Write MokAuth or MokDelAuth */ + /* Write MokAuth, MokDelAuth, MokXAuth, or MokXDelAuth */ if (!use_simple_hash) { var.Data = (void *)&pw_crypt; var.DataSize = PASSWORD_CRYPT_SIZE; @@ -930,9 +967,10 @@ done: } static int -is_valid_request (efi_guid_t type, void *mok, uint32_t mok_size, uint8_t import) +is_valid_request (efi_guid_t type, void *mok, uint32_t mok_size, MokRequest req) { - if (import) { + switch (req) { + case ENROLL_MOK: if (is_duplicate (type, mok, mok_size, EFI_GLOBAL_VARIABLE, "PK") || is_duplicate (type, mok, mok_size, EFI_GLOBAL_VARIABLE, "KEK") || is_duplicate (type, mok, mok_size, EFI_IMAGE_SECURITY_DATABASE_GUID, "db") || @@ -940,29 +978,63 @@ is_valid_request (efi_guid_t type, void *mok, uint32_t mok_size, uint8_t import) is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokNew")) { return 0; } - } else { + break; + case DELETE_MOK: if (!is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokListRT") || is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokDel")) { return 0; } + break; + case ENROLL_BLACKLIST: + if (is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokListXRT") || + is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokXNew")) { + return 0; + } + break; + case DELETE_BLACKLIST: + if (!is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokListXRT") || + is_duplicate (type, mok, mok_size, SHIM_LOCK_GUID, "MokXDel")) { + return 0; + } + break; } return 1; } static int -in_pending_request (void *mok, uint32_t mok_size, uint8_t import) +in_pending_request (void *mok, uint32_t mok_size, MokRequest req) { efi_variable_t authvar; - const char *var_name = import ? "MokDel" : "MokNew"; + const char *var_name; if (!mok || mok_size == 0) return 0; memset (&authvar, 0, sizeof(authvar)); - authvar.VariableName = import ? "MokDelAuth" : "MokAuth"; authvar.VendorGuid = SHIM_LOCK_GUID; + switch (req) { + case ENROLL_MOK: + var_name = "MokDel"; + authvar.VariableName = "MokDelAuth"; + break; + case DELETE_MOK: + var_name = "MokNew"; + authvar.VariableName = "MokAuth"; + break; + case ENROLL_BLACKLIST: + var_name = "MokXDel"; + authvar.VariableName = "MokXDelAuth"; + break; + case DELETE_BLACKLIST: + var_name = "MokXNew"; + authvar.VariableName = "MokXAuth"; + break; + default: + return 0; + } + if (read_variable (&authvar) != EFI_SUCCESS) { return 0; } @@ -979,7 +1051,7 @@ in_pending_request (void *mok, uint32_t mok_size, uint8_t import) } static int -issue_mok_request (char **files, uint32_t total, uint8_t import, +issue_mok_request (char **files, uint32_t total, MokRequest req, const char *hash_file, const int root_pw) { efi_variable_t old_req; @@ -999,7 +1071,22 @@ issue_mok_request (char **files, uint32_t total, uint8_t import, if (!files) return -1; - req_name = import ? "MokNew" : "MokDel"; + switch (req) { + case ENROLL_MOK: + req_name = "MokNew"; + break; + case DELETE_MOK: + req_name = "MokDel"; + break; + case ENROLL_BLACKLIST: + req_name = "MokXNew"; + break; + case DELETE_BLACKLIST: + req_name = "MokXDel"; + break; + default: + return -1; + } sizes = malloc (total * sizeof(uint32_t)); @@ -1068,11 +1155,29 @@ issue_mok_request (char **files, uint32_t total, uint8_t import, files[i]); } - if (is_valid_request (EfiCertX509Guid, ptr, sizes[i], import)) { + if (is_valid_request (EfiCertX509Guid, ptr, sizes[i], req)) { ptr += sizes[i]; real_size += sizes[i] + sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); - } else if (in_pending_request (ptr, sizes[i], import)) { - printf ("Removed %s from %s\n", files[i], import ? "MokDel" : "MokNew"); + } else if (in_pending_request (ptr, sizes[i], req)) { + const char *pending; + switch (req) { + case ENROLL_MOK: + pending = "MokDel"; + break; + case DELETE_MOK: + pending = "MokNew"; + break; + case ENROLL_BLACKLIST: + pending = "MokXDel"; + break; + case DELETE_BLACKLIST: + pending = "MokXNew"; + break; + default: + pending = ""; + break; + } + printf ("Removed %s from %s\n", files[i], pending); ptr -= sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); } else { @@ -1095,7 +1200,7 @@ issue_mok_request (char **files, uint32_t total, uint8_t import, real_size += old_req.DataSize; } - if (update_request (new_list, real_size, import, hash_file, root_pw) < 0) { + if (update_request (new_list, real_size, req, hash_file, root_pw) < 0) { goto error; } @@ -1115,29 +1220,58 @@ static int import_moks (char **files, uint32_t total, const char *hash_file, const int root_pw) { - return issue_mok_request (files, total, 1, hash_file, root_pw); + return issue_mok_request (files, total, ENROLL_MOK, hash_file, root_pw); } static int delete_moks (char **files, uint32_t total, const char *hash_file, const int root_pw) { - return issue_mok_request (files, total, 0, hash_file, root_pw); + return issue_mok_request (files, total, DELETE_MOK, hash_file, root_pw); } static int -revoke_request (uint8_t import) +import_blacklist (char **files, uint32_t total, const char *hash_file, + const int root_pw) { - if (import == 1) { + return issue_mok_request (files, total, ENROLL_BLACKLIST, hash_file, root_pw); +} + +static int +delete_blacklist (char **files, uint32_t total, const char *hash_file, + const int root_pw) +{ + return issue_mok_request (files, total, DELETE_BLACKLIST, hash_file, root_pw); +} + +static int +revoke_request (MokRequest req) +{ + switch (req) { + case ENROLL_MOK: if (test_and_delete_var ("MokNew") < 0) return -1; if (test_and_delete_var ("MokAuth") < 0) return -1; - } else { + break; + case DELETE_MOK: if (test_and_delete_var ("MokDel") < 0) return -1; if (test_and_delete_var ("MokDelAuth") < 0) return -1; + break; + case ENROLL_BLACKLIST: + if (test_and_delete_var ("MokXNew") < 0) + return -1; + if (test_and_delete_var ("MokXAuth") < 0) + return -1; + break; + case DELETE_BLACKLIST: + if (test_and_delete_var ("MokXDel") < 0) + return -1; + if (test_and_delete_var ("MokXDelAuth") < 0) + return -1; + break; } return 0; @@ -1419,7 +1553,7 @@ test_key (const char *key_file) goto error; } - if (!is_valid_request (EfiCertX509Guid, key, read_size, 1)) { + if (!is_valid_request (EfiCertX509Guid, key, read_size, ENROLL_MOK)) { printf ("%s is not enrolled\n", key_file); ret = 0; } else { @@ -1440,7 +1574,7 @@ error: static int reset_moks (const char *hash_file, const int root_pw) { - if (update_request (NULL, 0, 1, hash_file, root_pw)) { + if (update_request (NULL, 0, ENROLL_MOK, hash_file, root_pw)) { fprintf (stderr, "Failed to issue a reset request\n"); return -1; } @@ -1539,6 +1673,7 @@ main (int argc, char *argv[]) {"simple-hash", no_argument, 0, 's'}, {"ignore-db", no_argument, 0, 0 }, {"use-db", no_argument, 0, 0 }, + {"mokx", no_argument, 0, 0 }, {0, 0, 0, 0} }; @@ -1574,6 +1709,8 @@ main (int argc, char *argv[]) command |= IGNORE_DB; } else if (strcmp (option, "use-db") == 0) { command |= USE_DB; + } else if (strcmp (option, "mokx") == 0) { + command |= MOKX; } break; case 'd': @@ -1683,10 +1820,10 @@ main (int argc, char *argv[]) ret = delete_moks (files, total, hash_file, 0); break; case REVOKE_IMPORT: - ret = revoke_request (1); + ret = revoke_request (ENROLL_MOK); break; case REVOKE_DELETE: - ret = revoke_request (0); + ret = revoke_request (DELETE_MOK); break; case EXPORT: ret = export_moks (); @@ -1730,6 +1867,36 @@ main (int argc, char *argv[]) case USE_DB: ret = enable_db (); break; + case LIST_ENROLLED | MOKX: + ret = list_keys_in_var ("MokListXRT"); + break; + case LIST_NEW | MOKX: + ret = list_keys_in_var ("MokXNew"); + break; + case LIST_DELETE | MOKX: + ret = list_keys_in_var ("MokXDel"); + break; + case IMPORT | MOKX: + case IMPORT | SIMPLE_HASH | MOKX: + if (use_root_pw) + ret = import_blacklist (files, total, NULL, 1); + else + ret = import_blacklist (files, total, hash_file, 0); + break; + case DELETE | MOKX: + case DELETE | SIMPLE_HASH | MOKX: + if (use_root_pw) + ret = delete_blacklist (files, total, NULL, 1); + else + ret = delete_blacklist (files, total, hash_file, 0); + break; + case REVOKE_IMPORT | MOKX: + ret = revoke_request (ENROLL_BLACKLIST); + break; + case REVOKE_DELETE | MOKX: + ret = revoke_request (DELETE_BLACKLIST); + break; + default: print_help (); break; -- 1.8.1.4 From 73c2a558b6fa9fb42526d4d2ac5c7db40d402c8f Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 23 Oct 2013 10:41:58 +0800 Subject: [PATCH 07/18] Fix the memory leakage --- src/mokutil.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mokutil.c b/src/mokutil.c index f10e6e8..bcc12ca 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1746,10 +1746,18 @@ main (int argc, char *argv[]) break; case 'f': + if (hash_file) { + command |= HELP; + break; + } hash_file = strdup (optarg); break; case 'g': + if (input_pw) { + command |= HELP; + break; + } if (optarg) input_pw = strdup (optarg); @@ -1765,6 +1773,10 @@ main (int argc, char *argv[]) use_root_pw = 1; break; case 't': + if (key_file) { + command |= HELP; + break; + } key_file = strdup (optarg); if (key_file == NULL) { fprintf (stderr, "Could not allocate space: %m\n"); -- 1.8.1.4 From 62162fc5a5c33c987e4b8106a9e98c3abf8288ae Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 23 Oct 2013 17:29:53 +0800 Subject: [PATCH 08/18] Support import and delete a hash --- src/mokutil.c | 424 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 322 insertions(+), 102 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index bcc12ca..b8edf74 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -80,6 +80,8 @@ EFI_GUID (0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, #define IGNORE_DB (1 << 18) #define USE_DB (1 << 19) #define MOKX (1 << 20) +#define IMPORT_HASH (1 << 21) +#define DELETE_HASH (1 << 22) #define DEFAULT_CRYPT_METHOD SHA512_BASED #define DEFAULT_SALT_SIZE SHA512_SALT_MAX @@ -132,21 +134,27 @@ print_help () printf (" --generate-hash[=password]\t\tGenerate the password hash\n"); printf (" --ignore-db\t\t\t\tIgnore DB for validation\n"); printf (" --use-db\t\t\t\tUse DB for validation\n"); + printf (" --import-hash \t\t\tImport a hash\n"); + printf (" --delete-hash \t\t\tDelete a specific hash\n"); printf ("\n"); printf ("Supplimentary Options:\n"); printf (" --hash-file \t\tUse the specific password hash\n"); printf (" \t\t(For --import, --delete, --password,\n"); - printf (" \t\t and --reset)\n"); + printf (" \t\t --reset, --import-hash,\n"); + printf (" \t\t and --delete-hash)\n"); printf (" --root-pw\t\t\t\tUse the root password\n"); printf (" \t\t\t\t(For --import, --delete, --password,\n"); - printf (" \t\t\t\t and --reset)\n"); + printf (" \t\t\t\t --reset, --import-hash,\n"); + printf (" \t\t\t\t and --delete-hash)\n"); printf (" --simple-hash\t\t\t\tUse the old password hash method\n"); printf (" \t\t\t\t(For --import, --delete, --password,\n"); - printf (" \t\t\t\t --clear-password and --reset)\n"); + printf (" \t\t\t\t --clear-password, --reset,\n"); + printf (" \t\t\t\t --import-hash, and --delete-hash)\n"); printf (" --mokx\t\t\t\tManipulate the MOK blacklist\n"); printf (" \t\t\t\t(For --list-enrolled, --list-new,\n"); printf (" \t\t\t\t --list-delete, --import, --delete,\n"); - printf (" \t\t\t\t --revoke-import, and --revoke-delete)\n"); + printf (" \t\t\t\t --revoke-import, --revoke-delete,\n"); + printf (" \t\t\t\t --import-hash, and --delete-hash)\n"); } static int @@ -396,17 +404,50 @@ list_keys (efi_variable_t *var) return 0; } +/* match the hash in the hash array and return the index if matched */ static int -delete_key_from_list (void *mok, uint32_t mok_size, - const char *var_name, efi_guid_t guid) +match_hash_array (efi_guid_t hash_type, const void *hash, + const void *hash_array, const uint32_t array_size) +{ + uint32_t hash_size, hash_count; + uint32_t sig_size; + int i; + void *ptr; + + hash_size = efi_hash_size (hash_type); + if (!hash_size) + return -1; + + sig_size = hash_size + sizeof(efi_guid_t); + if ((array_size % sig_size) != 0) { + fprintf (stderr, "invalid hash array size\n"); + return -1; + } + + ptr = (void *)hash_array; + hash_count = array_size / sig_size; + for (i = 0; i < hash_count; i++) { + ptr += sizeof(efi_guid_t); + if (memcmp (ptr, hash, hash_size) == 0) + return i; + ptr += hash_size; + } + + return -1; +} + +static int +delete_data_from_list (efi_guid_t type, void *data, uint32_t data_size, + const char *var_name, efi_guid_t guid) { efi_variable_t var; MokListNode *list; uint32_t mok_num, total, remain; - void *ptr, *data = NULL; + void *end, *start = NULL; int i, del_ind, ret = 0; + uint32_t sig_list_size, sig_size; - if (!var_name || !mok || mok_size == 0) + if (!var_name || !data || data_size == 0) return 0; memset (&var, 0, sizeof(var)); @@ -422,30 +463,56 @@ delete_key_from_list (void *mok, uint32_t mok_size, if (list == NULL) goto done; + remain = total; for (i = 0; i < mok_num; i++) { - if (list[i].mok_size != mok_size) + remain -= list[i].header->SignatureListSize; + if (efi_guidcmp (list[i].header->SignatureType, type) != 0) continue; - if (memcmp (list[i].mok, mok, mok_size) == 0) { - /* Remove this key */ - del_ind = i; - data = (void *)list[i].header; - ptr = data + list[i].header->SignatureListSize; - total -= list[i].header->SignatureListSize; + sig_list_size = list[i].header->SignatureListSize; + + if (efi_guidcmp (type, EfiCertX509Guid) == 0) { + if (list[i].mok_size != data_size) + continue; + + if (memcmp (list[i].mok, data, data_size) == 0) { + /* Remove this key */ + start = (void *)list[i].header; + end = start + sig_list_size; + total -= sig_list_size; + break; + } + } else { + del_ind = match_hash_array (type, data, list[i].mok, + list[i].mok_size); + if (del_ind < 0) + continue; + + start = (void *)list[i].header; + sig_size = signature_size (type); + if (sig_list_size == (sizeof(EFI_SIGNATURE_LIST) + sig_size)) { + /* Only one hash in the list */ + end = start + sig_list_size; + total -= sig_list_size; + } else { + /* More than one hash in the list */ + start += sizeof(EFI_SIGNATURE_LIST) + sig_size * del_ind; + end = start + sig_size; + total -= sig_size; + remain += sig_list_size - sizeof(EFI_SIGNATURE_LIST) - + (del_ind + 1) * sig_size; + } break; } } - /* the key is not in this list */ - if (data == NULL) + /* the key or hash is not in this list */ + if (start == NULL) return 0; - /* Move the rest of the keys */ - remain = 0; - for (i = del_ind + 1; i < mok_num; i++) - remain += list[i].header->SignatureListSize; + /* remove the key or hash */ if (remain > 0) - memmove (data, ptr, remain); + memmove (start, end, remain); var.DataSize = total; var.Attributes = EFI_VARIABLE_NON_VOLATILE @@ -459,7 +526,8 @@ delete_key_from_list (void *mok, uint32_t mok_size, ret = 1; done: - free (list); + if (list) + free (list); free (var.Data); return ret; @@ -884,37 +952,6 @@ is_valid_cert (void *cert, uint32_t cert_size) } static int -match_hash_array (efi_guid_t hash_type, const void *hash, - const void *hash_array, const uint32_t array_size) -{ - uint32_t hash_size, hash_count; - uint32_t sig_size; - int i; - void *ptr; - - hash_size = efi_hash_size (hash_type); - if (!hash_size) - return 0; - - sig_size = hash_size + sizeof(efi_guid_t); - if ((array_size % sig_size) != 0) { - fprintf (stderr, "invalid hash array size\n"); - return 0; - } - - ptr = (void *)hash_array; - hash_count = array_size / sig_size; - for (i = 0; i < hash_count; i++) { - ptr += sizeof(efi_guid_t); - if (memcmp (ptr, hash, hash_size) == 0) - return 1; - ptr += hash_size; - } - - return 0; -} - -static int is_duplicate (efi_guid_t type, const void *data, const uint32_t data_size, efi_guid_t vendor, const char *db_name) { @@ -952,7 +989,7 @@ is_duplicate (efi_guid_t type, const void *data, const uint32_t data_size, } } else { if (match_hash_array (type, data, list[i].mok, - list[i].mok_size)) { + list[i].mok_size) >= 0) { ret = 1; break; } @@ -960,7 +997,8 @@ is_duplicate (efi_guid_t type, const void *data, const uint32_t data_size, } done: - free (list); + if (list) + free (list); free (var.Data); return ret; @@ -1003,12 +1041,13 @@ is_valid_request (efi_guid_t type, void *mok, uint32_t mok_size, MokRequest req) } static int -in_pending_request (void *mok, uint32_t mok_size, MokRequest req) +in_pending_request (efi_guid_t type, void *data, uint32_t data_size, + MokRequest req) { efi_variable_t authvar; const char *var_name; - if (!mok || mok_size == 0) + if (!data || data_size == 0) return 0; memset (&authvar, 0, sizeof(authvar)); @@ -1044,8 +1083,9 @@ in_pending_request (void *mok, uint32_t mok_size, MokRequest req) if (authvar.DataSize == SHA256_DIGEST_LENGTH) return 0; - if (delete_key_from_list (mok, mok_size, var_name, SHIM_LOCK_GUID)) - return 1; + if (delete_data_from_list (type, data, data_size, var_name, + SHIM_LOCK_GUID)) + return 1; return 0; } @@ -1158,7 +1198,7 @@ issue_mok_request (char **files, uint32_t total, MokRequest req, if (is_valid_request (EfiCertX509Guid, ptr, sizes[i], req)) { ptr += sizes[i]; real_size += sizes[i] + sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); - } else if (in_pending_request (ptr, sizes[i], req)) { + } else if (in_pending_request (EfiCertX509Guid, ptr, sizes[i], req)) { const char *pending; switch (req) { case ENROLL_MOK: @@ -1217,31 +1257,186 @@ error: } static int -import_moks (char **files, uint32_t total, const char *hash_file, - const int root_pw) +identify_hash_type (const char *hash_str, efi_guid_t *type) { - return issue_mok_request (files, total, ENROLL_MOK, hash_file, root_pw); -} + int len = strlen (hash_str); + int hash_size; + int i; -static int -delete_moks (char **files, uint32_t total, const char *hash_file, - const int root_pw) -{ - return issue_mok_request (files, total, DELETE_MOK, hash_file, root_pw); + for (i = 0; i < len; i++) { + if ((hash_str[i] > '9' || hash_str[i] < '0') && + (hash_str[i] > 'f' || hash_str[i] < 'a') && + (hash_str[i] > 'F' || hash_str[i] < 'A')) + return -1; + } + + switch (len) { + case SHA_DIGEST_LENGTH*2: + *type = EfiHashSha1Guid; + hash_size = SHA_DIGEST_LENGTH; + break; + case SHA224_DIGEST_LENGTH*2: + *type = EfiHashSha224Guid; + hash_size = SHA224_DIGEST_LENGTH; + break; + case SHA256_DIGEST_LENGTH*2: + *type = EfiHashSha256Guid; + hash_size = SHA256_DIGEST_LENGTH; + break; + case SHA384_DIGEST_LENGTH*2: + *type = EfiHashSha384Guid; + hash_size = SHA384_DIGEST_LENGTH; + break; + case SHA512_DIGEST_LENGTH*2: + *type = EfiHashSha512Guid; + hash_size = SHA512_DIGEST_LENGTH; + break; + default: + return -1; + } + + return hash_size; } static int -import_blacklist (char **files, uint32_t total, const char *hash_file, - const int root_pw) +hex_str_to_binary (const char *hex_str, uint8_t *array, int len) { - return issue_mok_request (files, total, ENROLL_BLACKLIST, hash_file, root_pw); + char *pos; + int i; + + if (!hex_str || !array) + return -1; + + pos = (char *)hex_str; + for (i = 0; i < len; i++) { + sscanf (pos, "%2hhx", &array[i]); + pos += 2; + } + + return 0; } static int -delete_blacklist (char **files, uint32_t total, const char *hash_file, - const int root_pw) +issue_hash_request (const char *hash_str, MokRequest req, + const char *hash_file, const int root_pw) { - return issue_mok_request (files, total, DELETE_BLACKLIST, hash_file, root_pw); + efi_variable_t old_req; + const char *req_name; + void *new_list = NULL; + void *ptr; + unsigned long list_size = 0; + uint32_t sig_list_size; + int ret = -1; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *CertData; + efi_guid_t hash_type; + uint8_t db_hash[SHA512_DIGEST_LENGTH]; + int hash_size; + uint8_t valid = 0; + + if (!hash_str) + return -1; + + hash_size = identify_hash_type (hash_str, &hash_type); + if (hash_size < 0) + return -1; + + if (hex_str_to_binary (hash_str, db_hash, hash_size) < 0) + return -1; + + switch (req) { + case ENROLL_MOK: + req_name = "MokNew"; + break; + case DELETE_MOK: + req_name = "MokDel"; + break; + case ENROLL_BLACKLIST: + req_name = "MokXNew"; + break; + case DELETE_BLACKLIST: + req_name = "MokXDel"; + break; + default: + return -1; + } + + sig_list_size = sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t) + hash_size; + list_size += sig_list_size; + + memset (&old_req, 0, sizeof(old_req)); + + old_req.VariableName = req_name; + old_req.VendorGuid = SHIM_LOCK_GUID; + if (read_variable (&old_req) == EFI_SUCCESS) + list_size += old_req.DataSize; + + new_list = malloc (list_size); + if (!new_list) { + fprintf (stderr, "Failed to allocate space for %s\n", req_name); + goto error; + } + ptr = new_list; + + CertList = ptr; + CertList->SignatureType = hash_type; + CertList->SignatureListSize = sig_list_size; + CertList->SignatureHeaderSize = 0; + CertList->SignatureSize = hash_size + sizeof(efi_guid_t); + + CertData = (EFI_SIGNATURE_DATA *)(((uint8_t *)ptr) + + sizeof(EFI_SIGNATURE_LIST)); + CertData->SignatureOwner = SHIM_LOCK_GUID; + memcpy (CertData->SignatureData, db_hash, hash_size); + + if (is_valid_request (hash_type, db_hash, hash_size, req)) { + valid = 1; + } else if (in_pending_request (hash_type, db_hash, hash_size, req)) { + const char *pending; + switch (req) { + case ENROLL_MOK: + pending = "MokDel"; + break; + case DELETE_MOK: + pending = "MokNew"; + break; + case ENROLL_BLACKLIST: + pending = "MokXDel"; + break; + case DELETE_BLACKLIST: + pending = "MokXNew"; + break; + default: + pending = ""; + break; + } + printf ("Removed hash from %s\n", pending); + } else { + printf ("Skip hash\n"); + } + + if (!valid) { + ret = 0; + goto error; + } + + /* append the keys to the previous request */ + if (old_req.Data) { + memcpy (new_list + sig_list_size, old_req.Data, old_req.DataSize); + } + + if (update_request (new_list, list_size, req, hash_file, root_pw) < 0) { + goto error; + } + + ret = 0; +error: + if (old_req.Data) + free (old_req.Data); + if (new_list) + free (new_list); + + return ret; } static int @@ -1641,6 +1836,7 @@ main (int argc, char *argv[]) char *key_file = NULL; char *hash_file = NULL; char *input_pw = NULL; + char *hash_str = NULL; const char *option; int c, i, f_ind, total = 0; unsigned int command = 0; @@ -1674,6 +1870,8 @@ main (int argc, char *argv[]) {"ignore-db", no_argument, 0, 0 }, {"use-db", no_argument, 0, 0 }, {"mokx", no_argument, 0, 0 }, + {"import-hash", required_argument, 0, 0 }, + {"delete-hash", required_argument, 0, 0 }, {0, 0, 0, 0} }; @@ -1711,6 +1909,20 @@ main (int argc, char *argv[]) command |= USE_DB; } else if (strcmp (option, "mokx") == 0) { command |= MOKX; + } else if (strcmp (option, "import-hash") == 0) { + command |= IMPORT_HASH; + if (hash_str) { + command |= HELP; + break; + } + hash_str = strdup (optarg); + } else if (strcmp (option, "delete-hash") == 0) { + command |= DELETE_HASH; + if (hash_str) { + command |= HELP; + break; + } + hash_str = strdup (optarg); } break; case 'd': @@ -1802,7 +2014,7 @@ main (int argc, char *argv[]) } if (use_root_pw == 1 && use_simple_hash == 1) - use_simple_hash = 0;; + use_simple_hash = 0; if (hash_file && use_root_pw) command |= HELP; @@ -1819,17 +2031,23 @@ main (int argc, char *argv[]) break; case IMPORT: case IMPORT | SIMPLE_HASH: - if (use_root_pw) - ret = import_moks (files, total, NULL, 1); - else - ret = import_moks (files, total, hash_file, 0); + ret = issue_mok_request (files, total, ENROLL_MOK, + hash_file, use_root_pw); break; case DELETE: case DELETE | SIMPLE_HASH: - if (use_root_pw) - ret = delete_moks (files, total, NULL, 1); - else - ret = delete_moks (files, total, hash_file, 0); + ret = issue_mok_request (files, total, DELETE_MOK, + hash_file, use_root_pw); + break; + case IMPORT_HASH: + case IMPORT_HASH | SIMPLE_HASH: + ret = issue_hash_request (hash_str, ENROLL_MOK, + hash_file, use_root_pw); + break; + case DELETE_HASH: + case DELETE_HASH | SIMPLE_HASH: + ret = issue_hash_request (hash_str, DELETE_MOK, + hash_file, use_root_pw); break; case REVOKE_IMPORT: ret = revoke_request (ENROLL_MOK); @@ -1842,10 +2060,7 @@ main (int argc, char *argv[]) break; case PASSWORD: case PASSWORD | SIMPLE_HASH: - if (use_root_pw) - ret = set_password (NULL, 1, 0); - else - ret = set_password (hash_file, 0, 0); + ret = set_password (hash_file, use_root_pw, 0); break; case CLEAR_PASSWORD: case CLEAR_PASSWORD | SIMPLE_HASH: @@ -1865,10 +2080,7 @@ main (int argc, char *argv[]) break; case RESET: case RESET | SIMPLE_HASH: - if (use_root_pw) - ret = reset_moks (NULL, 1); - else - ret = reset_moks (hash_file, 0); + ret = reset_moks (hash_file, use_root_pw); break; case GENERATE_PW_HASH: ret = generate_pw_hash (input_pw); @@ -1890,17 +2102,23 @@ main (int argc, char *argv[]) break; case IMPORT | MOKX: case IMPORT | SIMPLE_HASH | MOKX: - if (use_root_pw) - ret = import_blacklist (files, total, NULL, 1); - else - ret = import_blacklist (files, total, hash_file, 0); + ret = issue_mok_request (files, total, ENROLL_BLACKLIST, + hash_file, use_root_pw); break; case DELETE | MOKX: case DELETE | SIMPLE_HASH | MOKX: - if (use_root_pw) - ret = delete_blacklist (files, total, NULL, 1); - else - ret = delete_blacklist (files, total, hash_file, 0); + ret = issue_mok_request (files, total, DELETE_BLACKLIST, + hash_file, use_root_pw); + break; + case IMPORT_HASH | MOKX: + case IMPORT_HASH | SIMPLE_HASH | MOKX: + ret = issue_hash_request (hash_str, ENROLL_BLACKLIST, + hash_file, use_root_pw); + break; + case DELETE_HASH | MOKX: + case DELETE_HASH | SIMPLE_HASH | MOKX: + ret = issue_hash_request (hash_str, DELETE_BLACKLIST, + hash_file, use_root_pw); break; case REVOKE_IMPORT | MOKX: ret = revoke_request (ENROLL_BLACKLIST); @@ -1908,7 +2126,6 @@ main (int argc, char *argv[]) case REVOKE_DELETE | MOKX: ret = revoke_request (DELETE_BLACKLIST); break; - default: print_help (); break; @@ -1929,5 +2146,8 @@ main (int argc, char *argv[]) if (input_pw) free (input_pw); + if (hash_str) + free (hash_str); + return ret; } -- 1.8.1.4 From e852519aad00c669716c76db8908b89c6b5583e1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 23 Oct 2013 17:42:05 +0800 Subject: [PATCH 09/18] Reorganize issue_*_request --- src/mokutil.c | 75 +++++++++++++++++++---------------------------------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index b8edf74..862cfbf 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1096,6 +1096,7 @@ issue_mok_request (char **files, uint32_t total, MokRequest req, { efi_variable_t old_req; const char *req_name; + const char *reverse_req; void *new_list = NULL; void *ptr; struct stat buf; @@ -1114,15 +1115,19 @@ issue_mok_request (char **files, uint32_t total, MokRequest req, switch (req) { case ENROLL_MOK: req_name = "MokNew"; + reverse_req = "MokDel"; break; case DELETE_MOK: req_name = "MokDel"; + reverse_req = "MokNew"; break; case ENROLL_BLACKLIST: req_name = "MokXNew"; + reverse_req = "MokXDel"; break; case DELETE_BLACKLIST: req_name = "MokXDel"; + reverse_req = "MokXNew"; break; default: return -1; @@ -1199,26 +1204,7 @@ issue_mok_request (char **files, uint32_t total, MokRequest req, ptr += sizes[i]; real_size += sizes[i] + sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); } else if (in_pending_request (EfiCertX509Guid, ptr, sizes[i], req)) { - const char *pending; - switch (req) { - case ENROLL_MOK: - pending = "MokDel"; - break; - case DELETE_MOK: - pending = "MokNew"; - break; - case ENROLL_BLACKLIST: - pending = "MokXDel"; - break; - case DELETE_BLACKLIST: - pending = "MokXNew"; - break; - default: - pending = ""; - break; - } - printf ("Removed %s from %s\n", files[i], pending); - + printf ("Removed %s from %s\n", files[i], reverse_req); ptr -= sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t); } else { printf ("Skip %s\n", files[i]); @@ -1322,6 +1308,7 @@ issue_hash_request (const char *hash_str, MokRequest req, { efi_variable_t old_req; const char *req_name; + const char *reverse_req; void *new_list = NULL; void *ptr; unsigned long list_size = 0; @@ -1347,20 +1334,37 @@ issue_hash_request (const char *hash_str, MokRequest req, switch (req) { case ENROLL_MOK: req_name = "MokNew"; + reverse_req = "MokDel"; break; case DELETE_MOK: req_name = "MokDel"; + reverse_req = "MokNew"; break; case ENROLL_BLACKLIST: req_name = "MokXNew"; + reverse_req = "MokXDel"; break; case DELETE_BLACKLIST: req_name = "MokXDel"; + reverse_req = "MokXNew"; break; default: return -1; } + if (is_valid_request (hash_type, db_hash, hash_size, req)) { + valid = 1; + } else if (in_pending_request (hash_type, db_hash, hash_size, req)) { + printf ("Removed hash from %s\n", reverse_req); + } else { + printf ("Skip hash\n"); + } + + if (!valid) { + ret = 0; + goto error; + } + sig_list_size = sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t) + hash_size; list_size += sig_list_size; @@ -1389,37 +1393,6 @@ issue_hash_request (const char *hash_str, MokRequest req, CertData->SignatureOwner = SHIM_LOCK_GUID; memcpy (CertData->SignatureData, db_hash, hash_size); - if (is_valid_request (hash_type, db_hash, hash_size, req)) { - valid = 1; - } else if (in_pending_request (hash_type, db_hash, hash_size, req)) { - const char *pending; - switch (req) { - case ENROLL_MOK: - pending = "MokDel"; - break; - case DELETE_MOK: - pending = "MokNew"; - break; - case ENROLL_BLACKLIST: - pending = "MokXDel"; - break; - case DELETE_BLACKLIST: - pending = "MokXNew"; - break; - default: - pending = ""; - break; - } - printf ("Removed hash from %s\n", pending); - } else { - printf ("Skip hash\n"); - } - - if (!valid) { - ret = 0; - goto error; - } - /* append the keys to the previous request */ if (old_req.Data) { memcpy (new_list + sig_list_size, old_req.Data, old_req.DataSize); -- 1.8.1.4 From 8603b648095d847fbed56b956b0b5aeaa62f091a Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 23 Oct 2013 18:29:09 +0800 Subject: [PATCH 10/18] Merge the hash into an existed signature list --- src/mokutil.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index 862cfbf..f87ae7a 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1312,14 +1312,17 @@ issue_hash_request (const char *hash_str, MokRequest req, void *new_list = NULL; void *ptr; unsigned long list_size = 0; - uint32_t sig_list_size; + uint32_t sig_size, sig_list_size; int ret = -1; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *CertData; efi_guid_t hash_type; uint8_t db_hash[SHA512_DIGEST_LENGTH]; int hash_size; + int merge_ind = -1; uint8_t valid = 0; + MokListNode *mok_list = NULL; + uint32_t mok_num; if (!hash_str) return -1; @@ -1365,16 +1368,31 @@ issue_hash_request (const char *hash_str, MokRequest req, goto error; } - sig_list_size = sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t) + hash_size; - list_size += sig_list_size; - memset (&old_req, 0, sizeof(old_req)); old_req.VariableName = req_name; old_req.VendorGuid = SHIM_LOCK_GUID; - if (read_variable (&old_req) == EFI_SUCCESS) + if (read_variable (&old_req) == EFI_SUCCESS) { + int i; list_size += old_req.DataSize; + mok_list = build_mok_list (old_req.Data, old_req.DataSize, + &mok_num); + if (mok_list == NULL) + goto error; + + /* Check if there is a signature list with the same type */ + for (i = 0; i < mok_num; i++) { + if (efi_guidcmp (mok_list[i].header->SignatureType, + hash_type) == 0) { + merge_ind = i; + break; + } + } + } + + list_size += sizeof(EFI_SIGNATURE_LIST) + sizeof(efi_guid_t) + hash_size; + new_list = malloc (list_size); if (!new_list) { fprintf (stderr, "Failed to allocate space for %s\n", req_name); @@ -1382,20 +1400,53 @@ issue_hash_request (const char *hash_str, MokRequest req, } ptr = new_list; - CertList = ptr; - CertList->SignatureType = hash_type; - CertList->SignatureListSize = sig_list_size; - CertList->SignatureHeaderSize = 0; - CertList->SignatureSize = hash_size + sizeof(efi_guid_t); + if (merge_ind < 0) { + /* Create a new signature list for the hash */ + sig_list_size = sizeof(EFI_SIGNATURE_LIST) + + sizeof(efi_guid_t) + hash_size; + CertList = ptr; + CertList->SignatureType = hash_type; + CertList->SignatureListSize = sig_list_size; + CertList->SignatureHeaderSize = 0; + CertList->SignatureSize = hash_size + sizeof(efi_guid_t); - CertData = (EFI_SIGNATURE_DATA *)(((uint8_t *)ptr) + - sizeof(EFI_SIGNATURE_LIST)); - CertData->SignatureOwner = SHIM_LOCK_GUID; - memcpy (CertData->SignatureData, db_hash, hash_size); + CertData = (EFI_SIGNATURE_DATA *)(((uint8_t *)ptr) + + sizeof(EFI_SIGNATURE_LIST)); + CertData->SignatureOwner = SHIM_LOCK_GUID; + memcpy (CertData->SignatureData, db_hash, hash_size); - /* append the keys to the previous request */ - if (old_req.Data) { - memcpy (new_list + sig_list_size, old_req.Data, old_req.DataSize); + /* prepend the hash to the previous request */ + ptr += sig_list_size; + if (old_req.Data) { + memcpy (ptr, old_req.Data, old_req.DataSize); + } + } else { + /* Merge the hash into an existed signature list */ + int i; + + for (i = 0; i < merge_ind; i++) { + sig_list_size = mok_list[i].header->SignatureListSize; + memcpy (ptr, (void *)mok_list[i].header, sig_list_size); + ptr += sig_list_size; + } + + /* Append the hash to the list */ + i = merge_ind; + sig_list_size = mok_list[i].header->SignatureListSize; + sig_size = hash_size + sizeof(efi_guid_t); + mok_list[i].header->SignatureListSize += sig_size; + memcpy (ptr, (void *)mok_list[i].header, sig_list_size); + ptr += sig_list_size; + memcpy (ptr, (void *)&hash_type, sizeof(efi_guid_t)); + ptr += sizeof(efi_guid_t); + memcpy (ptr, db_hash, hash_size); + ptr += hash_size; + + for (i = merge_ind + 1; i < mok_num; i++) { + sig_list_size = mok_list[i].header->SignatureListSize; + memcpy (ptr, (void *)mok_list[i].header, sig_list_size); + ptr += sig_list_size; + } } if (update_request (new_list, list_size, req, hash_file, root_pw) < 0) { @@ -1406,6 +1457,8 @@ issue_hash_request (const char *hash_str, MokRequest req, error: if (old_req.Data) free (old_req.Data); + if (mok_list) + free (mok_list); if (new_list) free (new_list); -- 1.8.1.4 From d933eba21ebad708d85ff23a715a14a7d67f51a9 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Thu, 24 Oct 2013 17:54:18 +0800 Subject: [PATCH 11/18] Initialize the request variable to avoid the potential crash --- src/mokutil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index f87ae7a..880c38f 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1334,6 +1334,8 @@ issue_hash_request (const char *hash_str, MokRequest req, if (hex_str_to_binary (hash_str, db_hash, hash_size) < 0) return -1; + memset (&old_req, 0, sizeof(old_req)); + switch (req) { case ENROLL_MOK: req_name = "MokNew"; @@ -1368,8 +1370,6 @@ issue_hash_request (const char *hash_str, MokRequest req, goto error; } - memset (&old_req, 0, sizeof(old_req)); - old_req.VariableName = req_name; old_req.VendorGuid = SHIM_LOCK_GUID; if (read_variable (&old_req) == EFI_SUCCESS) { -- 1.8.1.4 From 6c9d5519172ca5f87a08d1a46105c2a68b9f4db7 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Fri, 25 Oct 2013 18:29:07 +0800 Subject: [PATCH 12/18] Make test-key and reset support MOK blacklist --- src/mokutil.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index 880c38f..8ff4b41 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -154,7 +154,8 @@ print_help () printf (" \t\t\t\t(For --list-enrolled, --list-new,\n"); printf (" \t\t\t\t --list-delete, --import, --delete,\n"); printf (" \t\t\t\t --revoke-import, --revoke-delete,\n"); - printf (" \t\t\t\t --import-hash, and --delete-hash)\n"); + printf (" \t\t\t\t --import-hash, --delete-hash,\n"); + printf (" \t\t\t\t --reset, and --test-key)\n"); } static int @@ -1756,7 +1757,7 @@ read_file(int fd, void **bufp, size_t *lenptr) { } static int -test_key (const char *key_file) +test_key (MokRequest req, const char *key_file) { void *key = NULL; size_t read_size; @@ -1774,7 +1775,7 @@ test_key (const char *key_file) goto error; } - if (!is_valid_request (EfiCertX509Guid, key, read_size, ENROLL_MOK)) { + if (!is_valid_request (EfiCertX509Guid, key, read_size, req)) { printf ("%s is not enrolled\n", key_file); ret = 0; } else { @@ -1793,9 +1794,9 @@ error: } static int -reset_moks (const char *hash_file, const int root_pw) +reset_moks (MokRequest req, const char *hash_file, const int root_pw) { - if (update_request (NULL, 0, ENROLL_MOK, hash_file, root_pw)) { + if (update_request (NULL, 0, req, hash_file, root_pw)) { fprintf (stderr, "Failed to issue a reset request\n"); return -1; } @@ -2102,11 +2103,11 @@ main (int argc, char *argv[]) ret = sb_state (); break; case TEST_KEY: - ret = test_key (key_file); + ret = test_key (ENROLL_MOK, key_file); break; case RESET: case RESET | SIMPLE_HASH: - ret = reset_moks (hash_file, use_root_pw); + ret = reset_moks (ENROLL_MOK, hash_file, use_root_pw); break; case GENERATE_PW_HASH: ret = generate_pw_hash (input_pw); @@ -2152,6 +2153,13 @@ main (int argc, char *argv[]) case REVOKE_DELETE | MOKX: ret = revoke_request (DELETE_BLACKLIST); break; + case RESET | MOKX: + case RESET | SIMPLE_HASH | MOKX: + ret = reset_moks (ENROLL_BLACKLIST, hash_file, use_root_pw); + break; + case TEST_KEY | MOKX: + ret = test_key (ENROLL_BLACKLIST, key_file); + break; default: print_help (); break; -- 1.8.1.4 From 0fdc023cf98addb23ae511b91c963619ec1e8e2d Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Wed, 30 Oct 2013 10:29:25 +0800 Subject: [PATCH 13/18] Set the verbosity for shim and MokManager --- src/mokutil.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/mokutil.c b/src/mokutil.c index 8ff4b41..cd3b622 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -82,6 +82,7 @@ EFI_GUID (0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, #define MOKX (1 << 20) #define IMPORT_HASH (1 << 21) #define DELETE_HASH (1 << 22) +#define VERBOSITY (1 << 23) #define DEFAULT_CRYPT_METHOD SHA512_BASED #define DEFAULT_SALT_SIZE SHA512_SALT_MAX @@ -136,6 +137,7 @@ print_help () printf (" --use-db\t\t\t\tUse DB for validation\n"); printf (" --import-hash \t\t\tImport a hash\n"); printf (" --delete-hash \t\t\tDelete a specific hash\n"); + printf (" --set-verbosity \t\tSet the verbosity bit for shim\n"); printf ("\n"); printf ("Supplimentary Options:\n"); printf (" --hash-file \t\tUse the specific password hash\n"); @@ -1856,6 +1858,31 @@ generate_pw_hash (const char *input_pw) return 0; } +static int +set_verbosity (uint8_t verbosity) +{ + efi_variable_t var; + + if (verbosity) { + var.VariableName = "SHIM_VERBOSE"; + var.VendorGuid = SHIM_LOCK_GUID; + var.Data = (void *)&verbosity; + var.DataSize = sizeof(uint8_t); + var.Attributes = EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS; + + if (edit_protected_variable (&var) != EFI_SUCCESS) { + fprintf (stderr, "Failed to set SHIM_VERBOSE\n"); + return -1; + } + } else { + return test_and_delete_var ("SHIM_VERBOSE"); + } + + return 0; +} + int main (int argc, char *argv[]) { @@ -1868,6 +1895,7 @@ main (int argc, char *argv[]) int c, i, f_ind, total = 0; unsigned int command = 0; int use_root_pw = 0; + uint8_t verbosity = 0; int ret = -1; use_simple_hash = 0; @@ -1899,6 +1927,7 @@ main (int argc, char *argv[]) {"mokx", no_argument, 0, 0 }, {"import-hash", required_argument, 0, 0 }, {"delete-hash", required_argument, 0, 0 }, + {"set-verbosity", required_argument, 0, 0 }, {0, 0, 0, 0} }; @@ -1950,6 +1979,14 @@ main (int argc, char *argv[]) break; } hash_str = strdup (optarg); + } else if (strcmp (option, "set-verbosity") == 0) { + command |= VERBOSITY; + if (strcmp (optarg, "true") == 0) + verbosity = 1; + else if (strcmp (optarg, "false") == 0) + verbosity = 0; + else + command |= HELP; } break; case 'd': @@ -2160,6 +2197,9 @@ main (int argc, char *argv[]) case TEST_KEY | MOKX: ret = test_key (ENROLL_BLACKLIST, key_file); break; + case VERBOSITY: + ret = set_verbosity (verbosity); + break; default: print_help (); break; -- 1.8.1.4 From 96dfa331c2067c3a44d6086ec86e6abb87f3c30f Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 26 Nov 2013 12:36:12 +0800 Subject: [PATCH 14/18] Update the help and manpage --- man/mokutil.1 | 30 +++++++++++++++++++++++++++--- src/mokutil.c | 4 ++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/man/mokutil.1 b/man/mokutil.1 index abfdebb..fa990e5 100644 --- a/man/mokutil.1 +++ b/man/mokutil.1 @@ -5,22 +5,27 @@ mokutil \- utility to manipulate machine owner keys .SH SYNOPSIS \fBmokutil\fR [--list-enrolled] + ([--mokx]) .br \fBmokutil\fR [--list-new] + ([--mokx]) .br \fBmokutil\fR [--list-delete] + ([--mokx]) .br \fBmokutil\fR [--import \fIkeylist\fR| -i \fIkeylist\fR] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s]) + [--simple-hash | -s] | [--mokx]) .br \fBmokutil\fR [--delete \fIkeylist\fR | -d \fIkeylist\fR] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s]) + [--simple-hash | -s] | [--mokx]) .br \fBmokutil\fR [--revoke-import] + ([--mokx]) .br \fBmokutil\fR [--revoke-delete] + ([--mokx]) .br \fBmokutil\fR [--export | -x] .br @@ -41,10 +46,16 @@ mokutil \- utility to manipulate machine owner keys .br \fBmokutil\fR [--reset] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s]) + [--simple-hash | -s] | [--mok]) .br \fBmokutil\fR [--generate-hash=\fIpassword\fR | -g\fIpassword\fR] .br +\fBmokutil\fR [--ignore-db] +.br +\fBmokutil\fR [--use-db] +.br +\fBmokutil\fR [--set-verbosity (\fItrue\fR | \fIfalse\fR)] +.br .SH DESCRIPTION \fBmokutil\fR is a tool to import or delete the machines owner keys @@ -108,3 +119,16 @@ Use the root password hash from /etc/shadow Use the old SHA256 password hash method to hash the password .br Note: --root-pw invalidates --simple-hash +.TP +\fB--ignore-db\fR +Tell shim to not use the keys in db to verify EFI images +.TP +\fB--use-db\fR +Tell shim to use the keys in db to verify EFI images (default) +.TP +\fB--mokx\fR +Manipulate the MOK blacklist (MOKX) instead of the MOK list +.TP +\fB--set-verbosity\fR +Set the SHIM_VERBOSE to make shim more or less verbose +.TP diff --git a/src/mokutil.c b/src/mokutil.c index cd3b622..a1f1213 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -135,8 +135,8 @@ print_help () printf (" --generate-hash[=password]\t\tGenerate the password hash\n"); printf (" --ignore-db\t\t\t\tIgnore DB for validation\n"); printf (" --use-db\t\t\t\tUse DB for validation\n"); - printf (" --import-hash \t\t\tImport a hash\n"); - printf (" --delete-hash \t\t\tDelete a specific hash\n"); + printf (" --import-hash \t\t\tImport a hash into MOK or MOKX\n"); + printf (" --delete-hash \t\t\tDelete a hash in MOK or MOKX\n"); printf (" --set-verbosity \t\tSet the verbosity bit for shim\n"); printf ("\n"); printf ("Supplimentary Options:\n"); -- 1.8.1.4 From e2ea0acb875247d70626545d5f3837f2a422af2f Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 26 Nov 2013 12:40:57 +0800 Subject: [PATCH 15/18] Make the help less verbose It's all in the manpage anyway. --- src/mokutil.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/mokutil.c b/src/mokutil.c index a1f1213..628df7d 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -141,23 +141,9 @@ print_help () printf ("\n"); printf ("Supplimentary Options:\n"); printf (" --hash-file \t\tUse the specific password hash\n"); - printf (" \t\t(For --import, --delete, --password,\n"); - printf (" \t\t --reset, --import-hash,\n"); - printf (" \t\t and --delete-hash)\n"); printf (" --root-pw\t\t\t\tUse the root password\n"); - printf (" \t\t\t\t(For --import, --delete, --password,\n"); - printf (" \t\t\t\t --reset, --import-hash,\n"); - printf (" \t\t\t\t and --delete-hash)\n"); printf (" --simple-hash\t\t\t\tUse the old password hash method\n"); - printf (" \t\t\t\t(For --import, --delete, --password,\n"); - printf (" \t\t\t\t --clear-password, --reset,\n"); - printf (" \t\t\t\t --import-hash, and --delete-hash)\n"); printf (" --mokx\t\t\t\tManipulate the MOK blacklist\n"); - printf (" \t\t\t\t(For --list-enrolled, --list-new,\n"); - printf (" \t\t\t\t --list-delete, --import, --delete,\n"); - printf (" \t\t\t\t --revoke-import, --revoke-delete,\n"); - printf (" \t\t\t\t --import-hash, --delete-hash,\n"); - printf (" \t\t\t\t --reset, and --test-key)\n"); } static int -- 1.8.1.4 From 72dd17981660747bc84b7ce643451110529ece38 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 26 Nov 2013 16:26:16 +0800 Subject: [PATCH 16/18] New options to list the firmware keys --- man/mokutil.1 | 20 +++++++++++++ src/mokutil.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/man/mokutil.1 b/man/mokutil.1 index fa990e5..86846b4 100644 --- a/man/mokutil.1 +++ b/man/mokutil.1 @@ -56,6 +56,14 @@ mokutil \- utility to manipulate machine owner keys .br \fBmokutil\fR [--set-verbosity (\fItrue\fR | \fIfalse\fR)] .br +\fBmokutil\fR [--pk] +.br +\fBmokutil\fR [--kek] +.br +\fBmokutil\fR [--db] +.br +\fBmokutil\fR [--dbx] +.br .SH DESCRIPTION \fBmokutil\fR is a tool to import or delete the machines owner keys @@ -132,3 +140,15 @@ Manipulate the MOK blacklist (MOKX) instead of the MOK list \fB--set-verbosity\fR Set the SHIM_VERBOSE to make shim more or less verbose .TP +\fB--pk\fR +List the keys in the public Platform Key (PK) +.TP +\fB--kek\fR +List the keys in the Key Exchange Key Signature database (KEK) +.TP +\fB--db\fR +List the keys in the secure boot signature store (db) +.TP +\fB--dbx\fR +List the keys in the secure boot blacklist signature store (dbx) +.TP diff --git a/src/mokutil.c b/src/mokutil.c index 628df7d..6f5aec4 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -97,6 +97,15 @@ typedef enum { ENROLL_BLACKLIST } MokRequest; +typedef enum { + MOK_LIST_RT = 0, + MOK_LIST_X_RT, + PK, + KEK, + DB, + DBX, +} DBName; + typedef struct { EFI_SIGNATURE_LIST *header; uint32_t mok_size; @@ -138,6 +147,10 @@ print_help () printf (" --import-hash \t\t\tImport a hash into MOK or MOKX\n"); printf (" --delete-hash \t\t\tDelete a hash in MOK or MOKX\n"); printf (" --set-verbosity \t\tSet the verbosity bit for shim\n"); + printf (" --pk\t\t\t\t\tList the keys in PK\n"); + printf (" --kek\t\t\t\t\tList the keys in KEK\n"); + printf (" --db\t\t\t\t\tList the keys in db\n"); + printf (" --dbx\t\t\t\t\tList the keys in dbx\n"); printf ("\n"); printf ("Supplimentary Options:\n"); printf (" --hash-file \t\tUse the specific password hash\n"); @@ -523,7 +536,7 @@ done: } static int -list_keys_in_var (const char *var_name) +list_keys_in_var (const char *var_name, const efi_guid_t guid) { efi_variable_t var; efi_status_t status; @@ -531,7 +544,7 @@ list_keys_in_var (const char *var_name) memset (&var, 0, sizeof(var)); var.VariableName = var_name; - var.VendorGuid = SHIM_LOCK_GUID; + var.VendorGuid = guid; status = read_variable (&var); if (status != EFI_SUCCESS) { @@ -1869,6 +1882,27 @@ set_verbosity (uint8_t verbosity) return 0; } +static inline int +list_db (DBName db_name) +{ + switch (db_name) { + case MOK_LIST_RT: + return list_keys_in_var ("MokListRT", SHIM_LOCK_GUID); + case MOK_LIST_X_RT: + return list_keys_in_var ("MokListXRT", SHIM_LOCK_GUID); + case PK: + return list_keys_in_var ("PK", EFI_GLOBAL_VARIABLE); + case KEK: + return list_keys_in_var ("KEK", EFI_GLOBAL_VARIABLE); + case DB: + return list_keys_in_var ("db", EFI_IMAGE_SECURITY_DATABASE_GUID); + case DBX: + return list_keys_in_var ("dbx", EFI_IMAGE_SECURITY_DATABASE_GUID); + } + + return -1; +} + int main (int argc, char *argv[]) { @@ -1882,6 +1916,7 @@ main (int argc, char *argv[]) unsigned int command = 0; int use_root_pw = 0; uint8_t verbosity = 0; + DBName db_name = MOK_LIST_RT; int ret = -1; use_simple_hash = 0; @@ -1914,6 +1949,10 @@ main (int argc, char *argv[]) {"import-hash", required_argument, 0, 0 }, {"delete-hash", required_argument, 0, 0 }, {"set-verbosity", required_argument, 0, 0 }, + {"pk", no_argument, 0, 0 }, + {"kek", no_argument, 0, 0 }, + {"db", no_argument, 0, 0 }, + {"dbx", no_argument, 0, 0 }, {0, 0, 0, 0} }; @@ -1950,7 +1989,12 @@ main (int argc, char *argv[]) } else if (strcmp (option, "use-db") == 0) { command |= USE_DB; } else if (strcmp (option, "mokx") == 0) { - command |= MOKX; + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= MOKX; + db_name = MOK_LIST_X_RT; + } } else if (strcmp (option, "import-hash") == 0) { command |= IMPORT_HASH; if (hash_str) { @@ -1973,7 +2017,36 @@ main (int argc, char *argv[]) verbosity = 0; else command |= HELP; + } else if (strcmp (option, "pk") == 0) { + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= LIST_ENROLLED; + db_name = PK; + } + } else if (strcmp (option, "kek") == 0) { + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= LIST_ENROLLED; + db_name = KEK; + } + } else if (strcmp (option, "db") == 0) { + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= LIST_ENROLLED; + db_name = DB; + } + } else if (strcmp (option, "dbx") == 0) { + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= LIST_ENROLLED; + db_name = DBX; + } } + break; case 'd': case 'i': @@ -2071,13 +2144,14 @@ main (int argc, char *argv[]) switch (command) { case LIST_ENROLLED: - ret = list_keys_in_var ("MokListRT"); + case LIST_ENROLLED | MOKX: + ret = list_db (db_name); break; case LIST_NEW: - ret = list_keys_in_var ("MokNew"); + ret = list_keys_in_var ("MokNew", SHIM_LOCK_GUID); break; case LIST_DELETE: - ret = list_keys_in_var ("MokDel"); + ret = list_keys_in_var ("MokDel", SHIM_LOCK_GUID); break; case IMPORT: case IMPORT | SIMPLE_HASH: @@ -2141,14 +2215,11 @@ main (int argc, char *argv[]) case USE_DB: ret = enable_db (); break; - case LIST_ENROLLED | MOKX: - ret = list_keys_in_var ("MokListXRT"); - break; case LIST_NEW | MOKX: - ret = list_keys_in_var ("MokXNew"); + ret = list_keys_in_var ("MokXNew", SHIM_LOCK_GUID); break; case LIST_DELETE | MOKX: - ret = list_keys_in_var ("MokXDel"); + ret = list_keys_in_var ("MokXDel", SHIM_LOCK_GUID); break; case IMPORT | MOKX: case IMPORT | SIMPLE_HASH | MOKX: -- 1.8.1.4 From 9820c083e2a9b605a59aae7bdf56992f63abf7b8 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 26 Nov 2013 16:49:14 +0800 Subject: [PATCH 17/18] Add more short options --- man/mokutil.1 | 54 +++++++++++++++++++++++++++++------------------------- src/mokutil.c | 42 +++++++++++++++++++++++------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/man/mokutil.1 b/man/mokutil.1 index 86846b4..02b346f 100644 --- a/man/mokutil.1 +++ b/man/mokutil.1 @@ -4,28 +4,28 @@ mokutil \- utility to manipulate machine owner keys .SH SYNOPSIS -\fBmokutil\fR [--list-enrolled] - ([--mokx]) +\fBmokutil\fR [--list-enrolled | -l] + ([--mokx | -X]) .br -\fBmokutil\fR [--list-new] - ([--mokx]) +\fBmokutil\fR [--list-new | -N] + ([--mokx | -X]) .br -\fBmokutil\fR [--list-delete] - ([--mokx]) +\fBmokutil\fR [--list-delete | -D] + ([--mokx | -X]) .br \fBmokutil\fR [--import \fIkeylist\fR| -i \fIkeylist\fR] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s] | [--mokx]) + [--simple-hash | -s] | [--mokx | -X]) .br \fBmokutil\fR [--delete \fIkeylist\fR | -d \fIkeylist\fR] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s] | [--mokx]) + [--simple-hash | -s] | [--mokx |- X]) .br \fBmokutil\fR [--revoke-import] - ([--mokx]) + ([--mokx | -X]) .br \fBmokutil\fR [--revoke-delete] - ([--mokx]) + ([--mokx | -X]) .br \fBmokutil\fR [--export | -x] .br @@ -42,11 +42,11 @@ mokutil \- utility to manipulate machine owner keys .br \fBmokutil\fR [--sb-state] .br -\fBmokutil\fR [--test-key | -t] ... +\fBmokutil\fR [--test-key \fIkeyfile\fR | -t \fIkeyfile\fR] .br \fBmokutil\fR [--reset] ([--hash-file \fIhashfile\fR | -f \fIhashfile\fR] | [--root-pw | -P] | - [--simple-hash | -s] | [--mok]) + [--simple-hash | -s] | [--mok | -X]) .br \fBmokutil\fR [--generate-hash=\fIpassword\fR | -g\fIpassword\fR] .br @@ -71,18 +71,22 @@ mokutil \- utility to manipulate machine owner keys .SH OPTIONS .TP -\fB--list-enrolled\fR +\fB-l, --list-enrolled\fR List the keys the already stored in the database .TP -\fB--list-new\fR +\fB-N, --list-new\fR List the keys to be enrolled .TP -\fB--list-delete\fR +\fB-D, --list-delete\fR List the keys to be deleted .TP -\fB--import\fR -Collect the followed files and form a request to shim. The files must be in DER -format. +\fB-i, --import\fR +Collect the followed files and form a enrolling request to shim. The files must +be in DER format. +.TP +\fB-d, --delete\fR +Collect the followed files and form a deleting request to shim. The files must be +in DER format. .TP \fB--revoke-import\fR Revoke the current import request (MokNew) @@ -90,13 +94,13 @@ Revoke the current import request (MokNew) \fB--revoke-delete\fR Revoke the current delete request (MokDel) .TP -\fB--export\fR +\fB-x, --export\fR Export the keys stored in MokListRT .TP -\fB--password\fR +\fB-p, --password\fR Setup the password for MokManager (MokPW) .TP -\fB--clear-password\fR +\fB-c, --clear-password\fR Clear the password for MokManager (MokPW) .TP \fB--disable-validation\fR @@ -108,7 +112,7 @@ Enable the validation process in shim \fB--sb-state\fR Show SecureBoot State .TP -\fB--test-key\fR +\fB-t, --test-key\fR Test if the key is enrolled or not .TP \fB--reset\fR @@ -120,10 +124,10 @@ Generate the password hash \fB--hash-file\fR Use the password hash from a specific file .TP -\fB--root-pw\fR +\fB-P, --root-pw\fR Use the root password hash from /etc/shadow .TP -\fB--simple-hash\fR +\fB-s, --simple-hash\fR Use the old SHA256 password hash method to hash the password .br Note: --root-pw invalidates --simple-hash @@ -134,7 +138,7 @@ Tell shim to not use the keys in db to verify EFI images \fB--use-db\fR Tell shim to use the keys in db to verify EFI images (default) .TP -\fB--mokx\fR +\fB-X, --mokx\fR Manipulate the MOK blacklist (MOKX) instead of the MOK list .TP \fB--set-verbosity\fR diff --git a/src/mokutil.c b/src/mokutil.c index 6f5aec4..6fe8ae2 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1924,9 +1924,9 @@ main (int argc, char *argv[]) while (1) { static struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"list-enrolled", no_argument, 0, 0 }, - {"list-new", no_argument, 0, 0 }, - {"list-delete", no_argument, 0, 0 }, + {"list-enrolled", no_argument, 0, 'l'}, + {"list-new", no_argument, 0, 'N'}, + {"list-delete", no_argument, 0, 'D'}, {"import", required_argument, 0, 'i'}, {"delete", required_argument, 0, 'd'}, {"revoke-import", no_argument, 0, 0 }, @@ -1945,7 +1945,7 @@ main (int argc, char *argv[]) {"simple-hash", no_argument, 0, 's'}, {"ignore-db", no_argument, 0, 0 }, {"use-db", no_argument, 0, 0 }, - {"mokx", no_argument, 0, 0 }, + {"mokx", no_argument, 0, 'X'}, {"import-hash", required_argument, 0, 0 }, {"delete-hash", required_argument, 0, 0 }, {"set-verbosity", required_argument, 0, 0 }, @@ -1957,7 +1957,7 @@ main (int argc, char *argv[]) }; int option_index = 0; - c = getopt_long (argc, argv, "cd:f:g::hi:pst:xP", + c = getopt_long (argc, argv, "cd:f:g::hi:lpst:xDNPX", long_options, &option_index); if (c == -1) @@ -1966,13 +1966,7 @@ main (int argc, char *argv[]) switch (c) { case 0: option = long_options[option_index].name; - if (strcmp (option, "list-enrolled") == 0) { - command |= LIST_ENROLLED; - } else if (strcmp (option, "list-new") == 0) { - command |= LIST_NEW; - } else if (strcmp (option, "list-delete") == 0) { - command |= LIST_DELETE; - } else if (strcmp (option, "revoke-import") == 0) { + if (strcmp (option, "revoke-import") == 0) { command |= REVOKE_IMPORT; } else if (strcmp (option, "revoke-delete") == 0) { command |= REVOKE_DELETE; @@ -1988,13 +1982,6 @@ main (int argc, char *argv[]) command |= IGNORE_DB; } else if (strcmp (option, "use-db") == 0) { command |= USE_DB; - } else if (strcmp (option, "mokx") == 0) { - if (db_name != MOK_LIST_RT) { - command |= HELP; - } else { - command |= MOKX; - db_name = MOK_LIST_X_RT; - } } else if (strcmp (option, "import-hash") == 0) { command |= IMPORT_HASH; if (hash_str) { @@ -2048,6 +2035,15 @@ main (int argc, char *argv[]) } break; + case 'l': + command |= LIST_ENROLLED; + break; + case 'N': + command |= LIST_NEW; + break; + case 'D': + command |= LIST_DELETE; + break; case 'd': case 'i': if (c == 'd') @@ -2127,6 +2123,14 @@ main (int argc, char *argv[]) command |= SIMPLE_HASH; use_simple_hash = 1; break; + case 'X': + if (db_name != MOK_LIST_RT) { + command |= HELP; + } else { + command |= MOKX; + db_name = MOK_LIST_X_RT; + } + break; case 'h': case '?': command |= HELP; -- 1.8.1.4 From 0ff8112146c355c1bc4eec57cdeb0aed4cc4065c Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 26 Nov 2013 18:13:20 +0800 Subject: [PATCH 18/18] Catch the error from strdup() --- src/mokutil.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/mokutil.c b/src/mokutil.c index 6fe8ae2..27b9585 100644 --- a/src/mokutil.c +++ b/src/mokutil.c @@ -1989,6 +1989,10 @@ main (int argc, char *argv[]) break; } hash_str = strdup (optarg); + if (hash_str == NULL) { + fprintf (stderr, "Could not allocate space: %m\n"); + exit(1); + } } else if (strcmp (option, "delete-hash") == 0) { command |= DELETE_HASH; if (hash_str) { @@ -1996,6 +2000,10 @@ main (int argc, char *argv[]) break; } hash_str = strdup (optarg); + if (hash_str == NULL) { + fprintf (stderr, "Could not allocate space: %m\n"); + exit(1); + } } else if (strcmp (option, "set-verbosity") == 0) { command |= VERBOSITY; if (strcmp (optarg, "true") == 0) @@ -2069,6 +2077,10 @@ main (int argc, char *argv[]) } files = malloc (total * sizeof (char *)); + if (files == NULL) { + fprintf (stderr, "Could not allocate space: %m\n"); + exit(1); + } for (i = 0; i < total; i++) { f_ind = i + optind - 1; files[i] = malloc (strlen(argv[f_ind]) + 1); @@ -2082,6 +2094,10 @@ main (int argc, char *argv[]) break; } hash_file = strdup (optarg); + if (hash_file == NULL) { + fprintf (stderr, "Could not allocate space: %m\n"); + exit(1); + } break; case 'g': @@ -2089,8 +2105,13 @@ main (int argc, char *argv[]) command |= HELP; break; } - if (optarg) + if (optarg) { input_pw = strdup (optarg); + if (input_pw == NULL) { + fprintf (stderr, "Could not allocate space: %m\n"); + exit(1); + } + } command |= GENERATE_PW_HASH; break; -- 1.8.1.4