[PATCH v5,4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..ca3b8f19466c 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option is enabled, the following openssl +command option should be added where for generating CodeSign extended key usage +in X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH v5,2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4b693da488f1..c9f8bca0b0d3 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -243,7 +243,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b531df2013c4..077bfef928b6 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /* * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH v5,3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH v5,1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 47accec68cb0..1ccaebe2a28b 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v5 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v5: Fixed the wording in module-signing.rst. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..ca3b8f19466c 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option is enabled, the following openssl +command option should be added where for generating CodeSign extended key usage +in X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4b693da488f1..c9f8bca0b0d3 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -243,7 +243,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b531df2013c4..077bfef928b6 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /* * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 47accec68cb0..1ccaebe2a28b 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v5 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v5: Fixed the wording in module-signing.rst. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..ca3b8f19466c 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option is enabled, the following openssl +command option should be added where for generating CodeSign extended key usage +in X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4b693da488f1..c9f8bca0b0d3 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -243,7 +243,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b531df2013c4..077bfef928b6 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /* * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 47accec68cb0..1ccaebe2a28b 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v5 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v5: Fixed the wording in module-signing.rst. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 47accec68cb0..1ccaebe2a28b 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..ca3b8f19466c 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option is enabled, the following openssl +command option should be added where for generating CodeSign extended key usage +in X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 4b693da488f1..c9f8bca0b0d3 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -243,7 +243,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b531df2013c4..077bfef928b6 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /* * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH v5 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v5: Fixed the wording in module-signing.rst. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..b57b30c7125f 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option be enabled, the following openssl +command option should be added for generating CodeSign extended key usage in +X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 798291177186..4104f5465d8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -242,7 +242,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 61af3c4d82cc..1d2318ff63db 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /** * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 948c5203ca9c..07a1b28460a2 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -29,6 +29,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v4 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH v4 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v4: Fixed the wording in patch description. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 798291177186..4104f5465d8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -242,7 +242,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 61af3c4d82cc..1d2318ff63db 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /** * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU is enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index 7d7c7c8a545c..b57b30c7125f 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option be enabled, the following openssl +command option should be added for generating CodeSign extended key usage in +X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 948c5203ca9c..07a1b28460a2 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -29,6 +29,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH 0/4] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v3: - Add codeSigning EKU to x509.genkey key generation config. - Add openssl command option example for generating CodeSign EKU to module-signing.rst document. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (4): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification modsign: Add codeSigning EKU when generating X.509 key generation config Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU Documentation/admin-guide/module-signing.rst | 6 + certs/Makefile | 1 + certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 +++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- crypto/asymmetric_keys/x509_cert_parser.c| 24 ++ include/crypto/pkcs7.h | 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 9 files changed, 83 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH 4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU
Add an openssl command option example for generating CodeSign extended key usage in X.509 when CONFIG_CHECK_CODESIGN_EKU be enabled. Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/module-signing.rst | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/admin-guide/module-signing.rst index f8b584179cff..bc184124d646 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -170,6 +170,12 @@ generate the public/private key files:: -config x509.genkey -outform PEM -out kernel_key.pem \ -keyout kernel_key.pem +When ``CONFIG_CHECK_CODESIGN_EKU`` option be enabled, the following openssl +command option should be added for generating CodeSign extended key usage in +X.509:: + +-addext "extendedKeyUsage=codeSigning" + The full pathname for the resulting kernel_key.pem file can then be specified in the ``CONFIG_MODULE_SIG_KEY`` option, and the certificate and key therein will be used instead of an autogenerated keypair. -- 2.16.4
[PATCH 3/4] modsign: Add codeSigning EKU when generating X.509 key generation config
Add codeSigning EKU to the X.509 key generation config for the build time autogenerated kernel key. Signed-off-by: "Lee, Chun-Yi" --- certs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/certs/Makefile b/certs/Makefile index f4c25b67aad9..1ef4d6ca43b7 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -88,6 +88,7 @@ $(obj)/x509.genkey: @echo >>$@ "keyUsage=digitalSignature" @echo >>$@ "subjectKeyIdentifier=hash" @echo >>$@ "authorityKeyIdentifier=keyid" + @echo >>$@ "extendedKeyUsage=codeSigning" endif # CONFIG_MODULE_SIG_KEY $(eval $(call config_filename,MODULE_SIG_KEY)) -- 2.16.4
[PATCH v2 2/4] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 798291177186..4104f5465d8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -242,7 +242,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 61af3c4d82cc..1d2318ff63db 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /** * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[PATCH v2 1/4] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 948c5203ca9c..07a1b28460a2 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -29,6 +29,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v2 1/2] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 52c9b455fc7d..65721313b265 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -497,6 +497,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -526,6 +528,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 948c5203ca9c..07a1b28460a2 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -29,6 +29,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 4462ed2c18cd..e20e8eb53b21 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -113,9 +113,14 @@ enum OID { OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */ OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[PATCH v2 0/2] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. v2: Changed the help wording in the Kconfig. Lee, Chun-Yi (2): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification certs/system_keyring.c| 2 +- crypto/asymmetric_keys/Kconfig| 9 crypto/asymmetric_keys/pkcs7_trust.c | 37 --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/pkcs7.h| 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 7 files changed, 76 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH v2 2/2] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 798291177186..4104f5465d8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -242,7 +242,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..1754812df989 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,13 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage when verifying the signature in PKCS#7. It affects kernel + module verification and kexec PE binary verification. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 61af3c4d82cc..1d2318ff63db 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /** * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, - stru
[RFC PATCH 2/2] PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification
This patch adds the logic for checking the CodeSigning extended key usage extenstion when verifying signature of kernel module or kexec PE binary in PKCS#7. Signed-off-by: "Lee, Chun-Yi" --- certs/system_keyring.c | 2 +- crypto/asymmetric_keys/Kconfig | 10 ++ crypto/asymmetric_keys/pkcs7_trust.c | 37 +--- include/crypto/pkcs7.h | 3 ++- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 798291177186..4104f5465d8a 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -242,7 +242,7 @@ int verify_pkcs7_message_sig(const void *data, size_t len, goto error; } } - ret = pkcs7_validate_trust(pkcs7, trusted_keys); + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage); if (ret < 0) { if (ret == -ENOKEY) pr_devel("PKCS#7 signature not signed with a trusted key\n"); diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 1f1f004dc757..6e3de0c3b5f0 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -96,4 +96,14 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary. +config CHECK_CODESIGN_EKU + bool "Check codeSigning extended key usage" + depends on PKCS7_MESSAGE_PARSER=y + depends on SYSTEM_DATA_VERIFICATION + help + This option provides support for checking the codeSigning extended + key usage extension when verifying the signature in PKCS#7. It + affects kernel module verification and kexec PE binary verification + now. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 61af3c4d82cc..1d2318ff63db 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -16,12 +16,36 @@ #include #include "pkcs7_parser.h" +#ifdef CONFIG_CHECK_CODESIGN_EKU +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + struct public_key *public_key = key->payload.data[asym_crypto]; + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_KEXEC_PE_SIGNATURE: + return !!(public_key->eku & EKU_codeSigning); + default: + break; + } + return true; +} +#else +static bool check_codesign_eku(struct key *key, +enum key_being_used_for usage) +{ + return true; +} +#endif + /** * Check the trust on one PKCS#7 SignedInfo block. */ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, - struct key *trust_keyring) + struct key *trust_keyring, + enum key_being_used_for usage) { struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; @@ -112,6 +136,12 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; matched: + if (!check_codesign_eku(key, usage)) { + pr_warn("sinfo %u: The signer %x key is not CodeSigning\n", + sinfo->index, key_serial(key)); + key_put(key); + return -ENOKEY; + } ret = verify_signature(key, sig); key_put(key); if (ret < 0) { @@ -156,7 +186,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * May also return -ENOMEM. */ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -struct key *trust_keyring) +struct key *trust_keyring, +enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *p; @@ -167,7 +198,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, p->seen = false; for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, usage); switch (ret) { case -ENOKEY: continue; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 38ec7f5f9041..b3b48240ba73 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -30,7 +30,8 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, * pkcs7_trust.c */ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, -
[RFC PATCH 1/2] X.509: Add CodeSigning extended key usage parsing
This patch adds the logic for parsing the CodeSign extended key usage extension in X.509. The parsing result will be set to the eku flag which is carried by public key. It can be used in the PKCS#7 verification. Signed-off-by: "Lee, Chun-Yi" --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 3 files changed, 30 insertions(+) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 26ec20ef4899..5179da8b7cd9 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -480,6 +480,8 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; + int i = 0; + enum OID oid; pr_debug("Extension: %u\n", ctx->last_oid); @@ -509,6 +511,28 @@ int x509_process_extension(void *context, size_t hdrlen, return 0; } + if (ctx->last_oid == OID_extKeyUsage) { + if (v[0] != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ) || + v[1] != vlen - 2) + return -EBADMSG; + i += 2; + + while (i < vlen) { + /* A 10 bytes EKU OID Octet blob = +* ASN1_OID + size byte + 8 bytes OID */ + if (v[i] != ASN1_OID || v[i + 1] != 8 || (i + 10) > vlen) + return -EBADMSG; + + oid = look_up_OID(v + i + 2, v[i + 1]); + if (oid == OID_codeSigning) { + ctx->cert->pub->eku |= EKU_codeSigning; + } + i += 10; + } + pr_debug("extKeyUsage: %d\n", ctx->cert->pub->eku); + return 0; + } + return 0; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 11f535cfb810..7c7342648260 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -28,6 +28,7 @@ struct public_key { bool key_is_private; const char *id_type; const char *pkey_algo; + unsigned int eku : 9; /* Extended Key Usage (9-bit) */ }; extern void public_key_free(struct public_key *key); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 657d6bf2c064..cd448e9b02fc 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -107,9 +107,14 @@ enum OID { OID_gostTC26Sign512B, /* 1.2.643.7.1.2.1.2.2 */ OID_gostTC26Sign512C, /* 1.2.643.7.1.2.1.2.3 */ + /* Extended key purpose OIDs [RFC 5280] */ + OID_codeSigning,/* 1.3.6.1.5.5.7.3.3 */ + OID__NR }; +#define EKU_codeSigning(1 << 2) + extern enum OID look_up_OID(const void *data, size_t datasize); extern int sprint_oid(const void *, size_t, char *, size_t); extern int sprint_OID(enum OID, char *, size_t); -- 2.16.4
[RFC PATCH 0/2] Check codeSigning extended key usage extension
NIAP PP_OS certification requests that the OS shall validate the CodeSigning extended key usage extension field for integrity verifiction of exectable code: https://www.niap-ccevs.org/MMO/PP/-442-/ FIA_X509_EXT.1.1 This patchset adds the logic for parsing the codeSigning EKU extension field in X.509. And checking the CodeSigning EKU when verifying signature of kernel module or kexec PE binary in PKCS#7. Lee, Chun-Yi (2): X.509: Add CodeSigning extended key usage parsing PKCS#7: Check codeSigning EKU for kernel module and kexec pe verification certs/system_keyring.c| 2 +- crypto/asymmetric_keys/Kconfig| 10 + crypto/asymmetric_keys/pkcs7_trust.c | 37 --- crypto/asymmetric_keys/x509_cert_parser.c | 24 include/crypto/pkcs7.h| 3 ++- include/crypto/public_key.h | 1 + include/linux/oid_registry.h | 5 + 7 files changed, 77 insertions(+), 5 deletions(-) -- 2.16.4
[PATCH] efi/efivars: Create efivars mount point in the registration of efivars abstraction
This patch moved the logic of creating efivars mount point to the registration of efivars abstraction. It's useful for userland to determine the availability of efivars filesystem by checking the existence of mount point. The 'efivars' platform device be created on generic EFI runtime services platform, so it can be used to determine the availability of efivarfs. But this approach is not available for google gsmi efivars abstraction. This patch be tested on Here on qemu-OVMF and qemu-uboot. Cc: Ard Biesheuvel Cc: Matthias Brugger Cc: Fabian Vogt Cc: Ilias Apalodimas Cc: Greg Kroah-Hartman Cc: Arthur Heymans Cc: Patrick Rudolph Signed-off-by: "Lee, Chun-Yi" --- drivers/firmware/efi/efi.c | 7 --- drivers/firmware/efi/vars.c | 17 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3aa07c3b5136..23c11a2a3f4d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -405,13 +405,6 @@ static int __init efisubsys_init(void) if (error) goto err_remove_group; - /* and the standard mountpoint for efivarfs */ - error = sysfs_create_mount_point(efi_kobj, "efivars"); - if (error) { - pr_err("efivars: Subsystem registration failed.\n"); - goto err_remove_group; - } - if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) efi_debugfs_init(); diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 973eef234b36..6fa7f288d635 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -1179,6 +1179,8 @@ int efivars_register(struct efivars *efivars, const struct efivar_operations *ops, struct kobject *kobject) { + int error; + if (down_interruptible(_lock)) return -EINTR; @@ -1191,6 +1193,19 @@ int efivars_register(struct efivars *efivars, up(_lock); + /* and the standard mountpoint for efivarfs */ + if (efi_kobj) { + error = sysfs_create_mount_point(efi_kobj, "efivars"); + if (error) { + if (down_interruptible(_lock)) + return -EINTR; + __efivars = NULL; + up(_lock); + pr_err("efivars: Subsystem registration failed.\n"); + return error; + } + } + return 0; } EXPORT_SYMBOL_GPL(efivars_register); @@ -1222,6 +1237,8 @@ int efivars_unregister(struct efivars *efivars) pr_info("Unregistered efivars operations\n"); __efivars = NULL; + if (efi_kobj) + sysfs_remove_mount_point(efi_kobj, "efivars"); rv = 0; out: -- 2.16.4
[PATCH v2] efi/efivars: Create efivars mount point via efivars abstraction
This patch creates efivars mount point when active efivars abstraction be set. It is useful for userland to determine the availability of efivars filesystem. Cc: Matthias Brugger Cc: Fabian Vogt Cc: Ilias Apalodimas Cc: Ard Biesheuvel Signed-off-by: "Lee, Chun-Yi" --- v2: Using efivars_kobject() helper instead of checking GetVariable or GetNextVariable EFI runtime services. Because the efivarfs code could be instantiated using a different efivars abstraction. drivers/firmware/efi/efi.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3aa07c3b5136..db483fc68501 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -405,11 +405,13 @@ static int __init efisubsys_init(void) if (error) goto err_remove_group; - /* and the standard mountpoint for efivarfs */ - error = sysfs_create_mount_point(efi_kobj, "efivars"); - if (error) { - pr_err("efivars: Subsystem registration failed.\n"); - goto err_remove_group; + if (efivars_kobject()) { + /* and the standard mountpoint for efivarfs */ + error = sysfs_create_mount_point(efi_kobj, "efivars"); + if (error) { + pr_err("efivars: Subsystem registration failed.\n"); + goto err_remove_group; + } } if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) -- 2.16.4
[PATCH v2] efi/efivars: Create efivars mount point via efivars abstraction
This patch creates efivars mount point when active efivars abstraction be set. It is useful for userland to determine the availability of efivars filesystem. v2: Using efivars_kobject() helper instead of checking GetVariable or GetNextVariable EFI runtime services. Because the efivarfs code could be instantiated using a different efivars abstraction. Cc: Matthias Brugger Cc: Fabian Vogt Cc: Ilias Apalodimas Cc: Ard Biesheuvel Signed-off-by: "Lee, Chun-Yi" --- drivers/firmware/efi/efi.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3aa07c3b5136..db483fc68501 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -405,11 +405,13 @@ static int __init efisubsys_init(void) if (error) goto err_remove_group; - /* and the standard mountpoint for efivarfs */ - error = sysfs_create_mount_point(efi_kobj, "efivars"); - if (error) { - pr_err("efivars: Subsystem registration failed.\n"); - goto err_remove_group; + if (efivars_kobject()) { + /* and the standard mountpoint for efivarfs */ + error = sysfs_create_mount_point(efi_kobj, "efivars"); + if (error) { + pr_err("efivars: Subsystem registration failed.\n"); + goto err_remove_group; + } } if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) -- 2.16.4
[PATCH] efi/efivars: create efivars mount point when get variable services are available
The efivars filesystem depends on GetVariable or GetNextVariable EFI runtime services. So the /sys/firmware/efi/efivars does not need to be created when GetVariable and GetNextVariable are not available. It is useful for userland to determine the availability of efivars filesystem. Cc: Ilias Apalodimas Cc: Ard Biesheuvel Signed-off-by: "Lee, Chun-Yi" --- drivers/firmware/efi/efi.c | 13 - 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index fdd1db025dbf..929fbf4dfd5d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -404,11 +404,14 @@ static int __init efisubsys_init(void) if (error) goto err_remove_group; - /* and the standard mountpoint for efivarfs */ - error = sysfs_create_mount_point(efi_kobj, "efivars"); - if (error) { - pr_err("efivars: Subsystem registration failed.\n"); - goto err_remove_group; + if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE | + EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) { + /* and the standard mountpoint for efivarfs */ + error = sysfs_create_mount_point(efi_kobj, "efivars"); + if (error) { + pr_err("efivars: Subsystem registration failed.\n"); + goto err_remove_group; + } } if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) -- 2.16.4
[PATCH 5/5 v2] PM / hibernate: An option to request that snapshot image must be authenticated
This kernel option is similar to the option for kernel module signature verification. When this option is unselected, kernel will be tainted by restored from a snapshot image without (valid) signature. When the option is selected, kernel will refuse the system to be restored from a unauthenticated image. The hibernation resume process will be stopped , the snapshot image will be discarded and system just boots as normal. The hibernation can be triggered without snapshot master key when this option is unselected. But kernel will be tainted after hibernation resume. v2: - Fixed wording in Kconfig Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/kernel-parameters.txt | 6 include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 11 +++ kernel/power/hibernate.c| 8 +++-- kernel/power/power.h| 5 kernel/power/snapshot.c | 40 +++-- kernel/power/user.c | 2 +- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 37e235be1d35..f103b5d4676f 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3991,6 +3991,12 @@ protect_image Turn on image protection during restoration (that will set all pages holding image data during restoration read-only). + enforce_authWhen HIBERNATION_ENC_AUTH is set, this means + that snapshot image without (valid) signature + will fail to restore. + Note that if HIBERNATION_ENC_AUTH_FORCE is + set, that is always true, so this option does + nothing. retain_initrd [RAM] Keep initrd memory after extraction diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d6aac75b51ba..61714489cb57 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -598,7 +598,8 @@ extern enum system_states { #define TAINT_LIVEPATCH15 #define TAINT_AUX 16 #define TAINT_RANDSTRUCT 17 -#define TAINT_FLAGS_COUNT 18 +#define TAINT_UNSAFE_HIBERNATE 18 +#define TAINT_FLAGS_COUNT 19 struct taint_flag { char c_true;/* character printed when tainted */ diff --git a/kernel/panic.c b/kernel/panic.c index d10c340c43b0..7179c6d8d38f 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -335,6 +335,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { [ TAINT_LIVEPATCH ] = { 'K', ' ', true }, [ TAINT_AUX ] = { 'X', ' ', true }, [ TAINT_RANDSTRUCT ]= { 'T', ' ', true }, + [ TAINT_UNSAFE_HIBERNATE ] = { 'H', ' ', true }, }; /** diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 506a3c5a7a0d..cf1896c32014 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -90,6 +90,17 @@ config HIBERNATION_ENC_AUTH as the master key of hibernation. The TPM trusted key depends on TPM. The security of user defined key relies on user space. +config HIBERNATION_ENC_AUTH_FORCE + bool "Require hibernate snapshot image to be validly signed" + depends on HIBERNATION_ENC_AUTH + help + This option will prevent that a snapshot image without (valid) + signature be restored. Without this option, an unauthenticated + snapshot image can be restored but the restored kernel will be + tainted, which also means that the hibernation can be triggered + without snapshot key but kernel will be tainted without this + option. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 5ac2ab6f4a0e..9515bad0897e 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -272,11 +272,11 @@ static int create_image(int platform_mode) int error; error = snapshot_prepare_hash(false); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_prepare_crypto(false, true); - if (error) + if (error && snapshot_is_enforce_auth()) goto finish_hash; error = dpm_suspend_end(PMSG_FREEZE); @@ -708,7 +708,7 @@ int hibernate(void)
[PATCH 4/5 v2] PM / hibernate: Erase the snapshot master key in snapshot pages
If the encryption key be guessed then the snapshot master key can also be grabbed from snapshot image. Which means that the authentication key can also be calculated. So kernel erases master key in snapshot pages. Because the master key in image kernel be erased, kernel uses the trampoline page to forward snapshot master key to image kernel. v2: - Add memory barrier after cleaning key initialized flag. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" --- kernel/power/power.h| 6 kernel/power/snapshot.c | 5 kernel/power/snapshot_key.c | 67 + 3 files changed, 78 insertions(+) diff --git a/kernel/power/power.h b/kernel/power/power.h index 41263fdd3a54..d2fc73b2e200 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -36,6 +36,7 @@ struct swsusp_info { struct trampoline { bool snapshot_key_valid; int sig_verify_ret; + u8 snapshot_key[SNAPSHOT_KEY_SIZE]; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -55,6 +56,9 @@ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); +extern void snapshot_key_page_erase(unsigned long pfn, void *buff_addr); +extern void snapshot_key_trampoline_backup(struct trampoline *t); +extern void snapshot_key_trampoline_restore(struct trampoline *t); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } @@ -62,6 +66,8 @@ static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } +static inline void snapshot_key_trampoline_backup(struct trampoline *t) {} +static inline void snapshot_key_trampoline_restore(struct trampoline *t) {} #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ #ifdef CONFIG_ARCH_HIBERNATION_HEADER diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index cd10ab5e4850..80ed8e7c5ed8 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1697,6 +1697,9 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page); } + /* Erase key data in snapshot */ + snapshot_key_page_erase(pfn, crypto_buffer); + /* Encrypt hashed page */ encrypt_data_page(crypto_buffer); @@ -2482,6 +2485,7 @@ void snapshot_init_trampoline(void) t = (struct trampoline *)trampoline_buff; init_sig_verify(t); + snapshot_key_trampoline_backup(t); pr_info("Hibernation trampoline page prepared\n"); } @@ -2505,6 +2509,7 @@ void snapshot_restore_trampoline(void) t = (struct trampoline *)trampoline_virt; handle_sig_verify(t); + snapshot_key_trampoline_restore(t); snapshot_free_trampoline(); } diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c index 3a569b505d8d..9d478c27d6b9 100644 --- a/kernel/power/snapshot_key.c +++ b/kernel/power/snapshot_key.c @@ -29,11 +29,27 @@ static struct snapshot_key { const char *key_name; bool initialized; unsigned int key_len; + unsigned long pfn; /* pfn of keyblob */ + unsigned long addr_offset; /* offset in page for keyblob */ u8 key[SNAPSHOT_KEY_SIZE]; + u8 fingerprint[SHA512_DIGEST_SIZE]; /* fingerprint of keyblob */ } skey = { .key_name = "swsusp-kmk", }; +static void snapshot_key_clean(void) +{ + crypto_free_shash(hash_tfm); + hash_tfm = NULL; + skey.initialized = false; + barrier(); + skey.pfn = 0; + skey.key_len = 0; + skey.addr_offset = 0; + memzero_explicit(skey.key, SNAPSHOT_KEY_SIZE); + memzero_explicit(skey.fingerprint, SHA512_DIGEST_SIZE); +} + static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, bool may_sleep) { @@ -81,6 +97,53 @@ static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, return ret; } +static int get_key_fingerprint(u8 *fingerprint, u8 *key, unsigned int key_len, + bool may_sleep) +{ + return calc_key_hash(key, key_len, "FINGERPRINT", fingerprint, may_sleep); +} + +void snapshot_key_page_erase(unsigned long pfn, void *buff_addr) +{ + if (!skey.initialized || pfn != skey.pfn) + return; + + /* erase key data from snapshot buff
[PATCH 3/5] PM / hibernate: Encrypt snapshot image
To protect the secret in memory snapshot image, this patch adds the logic to encrypt snapshot pages by AES-CTR. Using AES-CTR is because it's simple, fast and parallelizable. But this patch didn't implement parallel encryption. The encrypt key is derived from the snapshot key. And the initialization vector will be kept in snapshot header for resuming. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 8 ++- kernel/power/power.h | 6 ++ kernel/power/snapshot.c | 154 ++- 3 files changed, 164 insertions(+), 4 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 0dda6a9f0af1..5ac2ab6f4a0e 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -275,10 +275,14 @@ static int create_image(int platform_mode) if (error) return error; + error = snapshot_prepare_crypto(false, true); + if (error) + goto finish_hash; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - goto finish_hash; + goto finish_crypto; } error = platform_pre_snapshot(platform_mode); @@ -335,6 +339,8 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_crypto: + snapshot_finish_crypto(); finish_hash: snapshot_finish_hash(); diff --git a/kernel/power/power.h b/kernel/power/power.h index c614b0a294e3..41263fdd3a54 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -5,6 +5,7 @@ #include #include #include +#include /* The max size of encrypted key blob */ #define KEY_BLOB_BUFF_LEN 512 @@ -24,6 +25,7 @@ struct swsusp_info { unsigned long pages; unsigned long size; unsigned long trampoline_pfn; + u8 iv[AES_BLOCK_SIZE]; u8 signature[SNAPSHOT_DIGEST_SIZE]; } __aligned(PAGE_SIZE); @@ -44,6 +46,8 @@ extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH /* kernel/power/snapshot.c */ extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv); +extern void snapshot_finish_crypto(void); extern int snapshot_prepare_hash(bool may_sleep); extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ @@ -53,6 +57,8 @@ extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } +static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index e817c035f378..cd10ab5e4850 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -41,7 +41,11 @@ #include #include #ifdef CONFIG_HIBERNATION_ENC_AUTH +#include +#include +#include #include +#include #endif #include "power.h" @@ -1413,6 +1417,127 @@ static unsigned int nr_copy_pages; static void **h_buf; #ifdef CONFIG_HIBERNATION_ENC_AUTH +static struct skcipher_request *sk_req; +static u8 iv[AES_BLOCK_SIZE]; +static void *c_buffer; + +static void init_iv(struct swsusp_info *info) +{ + memcpy(info->iv, iv, AES_BLOCK_SIZE); +} + +static void load_iv(struct swsusp_info *info) +{ + memcpy(iv, info->iv, AES_BLOCK_SIZE); +} + +int snapshot_prepare_crypto(bool may_sleep, bool create_iv) +{ + char enc_key[DERIVED_KEY_SIZE]; + struct crypto_skcipher *tfm; + int ret = 0; + + ret = snapshot_get_enc_key(enc_key, may_sleep); + if (ret) { + pr_warn_once("enc key is invalid\n"); + return -EINVAL; + } + + c_buffer = (void *)get_zeroed_page(GFP_KERNEL); + if (!c_buffer) { + pr_err("Allocate crypto buffer page failed\n"); + return -ENOMEM; + } + + tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + pr_err("failed to allocate skcipher (%d)\n", ret); + goto alloc_fail; + } + + ret = crypto_skcipher_setkey(tfm, enc_key, AES_MAX_KEY_SIZE); + if (ret) {
[PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image
When producing memory snapshot image, hibernation uses HMAC-SHA512 with snapshot key (from TPM trusted key) to calculate the hash of all data pages in snapshot image. The hash result will be kept in the snapshot header as the image signature. Before hibernation restores image, kernel executes HMAC-SHA512 again and compares the result with the signature in the header to verify the integrity of snapshot image. If the verification failed, the resume process will be stopped. Then the snapshot image will be discarded and system will boot as normal. On the other hand, a trampoline page be created in snapshot image when hibernation. This trampoline page be used to forward the state of snapshot key and the result of snapshot image verification from boot kernel to image kernel when resuming. The trampoline page will also be used to forward the snapshot key in the later patch. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 18 ++- kernel/power/power.h | 26 kernel/power/snapshot.c | 387 +-- kernel/power/swap.c | 6 + kernel/power/user.c | 12 ++ 5 files changed, 432 insertions(+), 17 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index ecc31e8e40d0..0dda6a9f0af1 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -271,10 +271,14 @@ static int create_image(int platform_mode) { int error; + error = snapshot_prepare_hash(false); + if (error) + return error; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - return error; + goto finish_hash; } error = platform_pre_snapshot(platform_mode); @@ -331,6 +335,9 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_hash: + snapshot_finish_hash(); + return error; } @@ -694,6 +701,14 @@ int hibernate(void) return -EPERM; } + error = snapshot_key_init(); + if (error) + return error; + + error = snapshot_create_trampoline(); + if (error) + return error; + lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(_device_available, -1, 0)) { @@ -750,6 +765,7 @@ int hibernate(void) pm_restore_gfp_mask(); } else { pm_pr_dbg("Image restored successfully.\n"); + snapshot_restore_trampoline(); } Free_bitmaps: diff --git a/kernel/power/power.h b/kernel/power/power.h index fe2dfa0d4d36..c614b0a294e3 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -11,6 +11,10 @@ #define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE #define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE +/* HMAC algorithm for hibernate snapshot signature */ +#define SNAPSHOT_HMAC "hmac(sha512)" +#define SNAPSHOT_DIGEST_SIZE SHA512_DIGEST_SIZE + struct swsusp_info { struct new_utsname uts; u32 version_code; @@ -19,6 +23,17 @@ struct swsusp_info { unsigned long image_pages; unsigned long pages; unsigned long size; + unsigned long trampoline_pfn; + u8 signature[SNAPSHOT_DIGEST_SIZE]; +} __aligned(PAGE_SIZE); + +/* + * The trampoline page is used to forward information + * from boot kernel to image kernel in restore stage. + */ +struct trampoline { + bool snapshot_key_valid; + int sig_verify_ret; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -27,12 +42,19 @@ extern void __init hibernate_reserved_size_init(void); extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH +/* kernel/power/snapshot.c */ +extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_hash(bool may_sleep); +extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else +static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } +static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ @@ -171,6 +193,10 @@ extern int snapshot_read_
[PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler
This patch adds a snapshot keys handler for using the key retention service api to create keys for snapshot image encryption and authentication. This handler uses TPM trusted key as the snapshot master key, and the encryption key and authentication key are derived from the snapshot key. The user defined key can also be used as the snapshot master key , but user must be aware that the security of user key relies on user space. The name of snapshot key is fixed to "swsusp-kmk". User should use the keyctl tool to load the key blob to root's user keyring. e.g. # /bin/keyctl add trusted swsusp-kmk "load `cat swsusp-kmk.blob`" @u or create a new user key. e.g. # /bin/keyctl add user swsusp-kmk password @u Then the disk_kmk sysfs file can be used to trigger the initialization of snapshot key: # echo 1 > /sys/power/disk_kmk After the initialization be triggered, the secret in the payload of swsusp-key will be copied by hibernation and be erased. Then user can use keyctl to remove swsusp-kmk key from root's keyring. If user does not trigger the initialization by disk_kmk file after swsusp-kmk be loaded to kernel. Then the snapshot key will be initialled when hibernation be triggered. v2: - Fixed bug of trusted_key_init's return value. - Fixed wording in Kconfig - Removed VLA usage - Removed the checking of capability for writing disk_kmk. - Fixed Kconfig, select trusted key. - Add memory barrier before setting key initialized flag. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" --- kernel/power/Kconfig| 14 +++ kernel/power/Makefile | 1 + kernel/power/hibernate.c| 33 ++ kernel/power/power.h| 16 +++ kernel/power/snapshot_key.c | 245 5 files changed, 309 insertions(+) create mode 100644 kernel/power/snapshot_key.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index f8fe57d1022e..506a3c5a7a0d 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -76,6 +76,20 @@ config HIBERNATION For more information take a look at . +config HIBERNATION_ENC_AUTH + bool "Hibernation encryption and authentication" + depends on HIBERNATION + select TRUSTED_KEYS + select CRYPTO_AES + select CRYPTO_HMAC + select CRYPTO_SHA512 + help + This option will encrypt and authenticate the memory snapshot image + of hibernation. It prevents that the snapshot image be arbitrarily + modified. A user can use the TPM's trusted key or user defined key + as the master key of hibernation. The TPM trusted key depends on TPM. + The security of user defined key relies on user space. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index e7e47d9be1e5..d949adbaf580 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o +obj-$(CONFIG_HIBERNATION_ENC_AUTH) += snapshot_key.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index abef759de7c8..ecc31e8e40d0 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1034,6 +1034,36 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(disk); +#ifdef CONFIG_HIBERNATION_ENC_AUTH +static ssize_t disk_kmk_show(struct kobject *kobj, struct kobj_attribute *attr, +char *buf) +{ + if (snapshot_key_initialized()) + return sprintf(buf, "initialized\n"); + else + return sprintf(buf, "uninitialized\n"); +} + +static ssize_t disk_kmk_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = 0; + char *p; + int len; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + if (strncmp(buf, "1", len)) + return -EINVAL; + + error = snapshot_key_init(); + + return error ? error : n; +} + +power_attr(disk_kmk); +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1138,6 +1168,9 @@ power_attr(reserved_size); static struct attribute * g[] = { _attr.attr, +#ifdef CONFIG_HIBERNATION_ENC_AUTH + _kmk_attr.attr, +#endif _offset_attr.attr, _attr.attr, _size_att
[PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image
Hi, This patchset is the implementation of encryption and authentication for hibernate snapshot image. The image will be encrypted by AES and authenticated by HMAC. The hibernate function can be used to snapshot memory pages to an image, then kernel restores the image to memory space in a appropriate time. There have secrets in snapshot image and cracker may modifies it for hacking system. Encryption and authentication of snapshot image can protect the system. Hibernate function requests the master key through key retention service. The snapshot master key can be a trusted key or a user defined key. The name of snapshot master key is fixed to "swsusp-kmk". User should loads swsusp-kmk to kernel by keyctl tool before the hibernation resume. e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume The TPM trusted key type is preferred to be the master key. But user defined key can also be used for testing or when the platform doesn't have TPM. User must be aware that the security of user key relies on user space. If the root account be compromised, then the user key will easy to be grabbed. v2: - Fixed bug of trusted_key_init's return value. - Fixed wording in Kconfig - Removed VLA usage - Removed the checking of capability for writing disk_kmk. - Fixed Kconfig, select trusted key. - Add memory barrier before setting key initialized flag. - Add memory barrier after cleaning key initialized flag. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Cc: Randy Dunlap Cc: Jann Horn Cc: Andy Lutomirski Signed-off-by: "Lee, Chun-Yi" Lee, Chun-Yi (5): PM / hibernate: Create snapshot keys handler PM / hibernate: Generate and verify signature for snapshot image PM / hibernate: Encrypt snapshot image PM / hibernate: Erase the snapshot master key in snapshot pages PM / hibernate: An option to request that snapshot image must be authenticated Documentation/admin-guide/kernel-parameters.txt | 6 + include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 25 + kernel/power/Makefile | 1 + kernel/power/hibernate.c| 59 ++- kernel/power/power.h| 59 +++ kernel/power/snapshot.c | 576 +++- kernel/power/snapshot_key.c | 312 + kernel/power/swap.c | 6 + kernel/power/user.c | 12 + 11 files changed, 1042 insertions(+), 18 deletions(-) create mode 100644 kernel/power/snapshot_key.c -- 2.13.6
[PATCH 2/2] PM / Sleep: Check the file capability when writing wake lock interface
The wake lock/unlock sysfs interfaces check that the writer must has CAP_BLOCK_SUSPEND capability. But the checking logic can be bypassed by opening sysfs file within an unprivileged process and then writing the file within a privileged process. The tricking way has been exposed by Andy Lutomirski in CVE-2013-1959. Here is an example that it's a simplified version from CVE-2013-1959 to bypass the capability checking of wake_lock sysfs: int main(int argc, char* argv[]) { int fd, ret = 0; fd = open("/sys/power/wake_lock", O_RDWR); if (fd < 0) err(1, "open wake_lock"); if (dup2(fd, 1) != 1) err(1, "dup2"); sleep(1); execl("./string", "string"); return ret; } The string is a privileged program that it can be used to write string to wake_lock interface. The main unprivileged process opens the sysfs interface and overwrites stdout. So the privileged process will write to wake_lock. This patch implemented the callback of file capable checking hook in kobject attribute level. It will check the opener's capability. Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Chen Yu Cc: Giovanni Gherdovich Cc: Jann Horn Cc: Andy Lutomirski Cc: Pavel Machek Cc: Len Brown Cc: "Martin K. Petersen" Cc: Randy Dunlap Cc: Joe Perches Cc: Bart Van Assche Signed-off-by: "Lee, Chun-Yi" --- kernel/power/main.c | 14 ++ kernel/power/wakelock.c | 6 -- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/kernel/power/main.c b/kernel/power/main.c index 98e76cad128b..265199efedc1 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -661,6 +661,11 @@ static ssize_t wake_lock_store(struct kobject *kobj, return error ? error : n; } +static bool wake_lock_store_file_capable(const struct file *file) +{ + return file_ns_capable(file, _user_ns, CAP_BLOCK_SUSPEND); +} + power_attr(wake_lock); static ssize_t wake_unlock_show(struct kobject *kobj, @@ -678,6 +683,11 @@ static ssize_t wake_unlock_store(struct kobject *kobj, return error ? error : n; } +static bool wake_unlock_store_file_capable(const struct file *file) +{ + return file_ns_capable(file, _user_ns, CAP_BLOCK_SUSPEND); +} + power_attr(wake_unlock); #endif /* CONFIG_PM_WAKELOCKS */ @@ -803,6 +813,10 @@ static int __init pm_init(void) power_kobj = kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; +#ifdef CONFIG_PM_WAKELOCKS + wake_lock_attr.store_file_capable = wake_lock_store_file_capable; + wake_unlock_attr.store_file_capable = wake_unlock_store_file_capable; +#endif error = sysfs_create_group(power_kobj, _group); if (error) return error; diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index 4210152e56f0..52a4cfe55eb5 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -205,9 +205,6 @@ int pm_wake_lock(const char *buf) size_t len; int ret = 0; - if (!capable(CAP_BLOCK_SUSPEND)) - return -EPERM; - while (*str && !isspace(*str)) str++; @@ -251,9 +248,6 @@ int pm_wake_unlock(const char *buf) size_t len; int ret = 0; - if (!capable(CAP_BLOCK_SUSPEND)) - return -EPERM; - len = strlen(buf); if (!len) return -EINVAL; -- 2.13.6
[PATCH 1/2] sysfs: Add hook for checking the file capable for opener
Base on the discussion in the following mail loop about checking capability in sysfs write handler: https://lkml.org/lkml/2018/9/13/978 Some sysfs write handler are checking the writer's capability by using capable(). Base on CVE-2013-1959, those code should use file_ns_capable() to check the opener's capability. Otherwise the capability checking logic can be bypassed. This patch adds hook to sysfs_ops that it allows different implementation in object and attribute levels for checking file capable before accessing sysfs interfaces. The callback function in kobject sysfs_ops is the first implementation of new hook. It casts attribute to kobj_attribute and calls the file capability callback function in kobject attribute level. Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Chen Yu Cc: Giovanni Gherdovich Cc: Jann Horn Cc: Andy Lutomirski Cc: Pavel Machek Cc: Len Brown Cc: "Martin K. Petersen" Cc: Randy Dunlap Cc: Joe Perches Cc: Bart Van Assche Signed-off-by: "Lee, Chun-Yi" --- fs/sysfs/file.c | 8 include/linux/kobject.h | 2 ++ include/linux/sysfs.h | 2 ++ lib/kobject.c | 26 ++ 4 files changed, 38 insertions(+) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index bb71db63c99c..cf98957babd0 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -58,6 +58,10 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) * if @ops->show() isn't implemented. */ if (ops->show) { + if (ops->show_file_capable && + !ops->show_file_capable(of->file, of->kn->priv)) + return -EPERM; + count = ops->show(kobj, of->kn->priv, buf); if (count < 0) return count; @@ -136,6 +140,10 @@ static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, if (!count) return 0; + if (ops->store_file_capable && + !ops->store_file_capable(of->file, of->kn->priv)) + return -EPERM; + return ops->store(kobj, of->kn->priv, buf, count); } diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 1ab0d624fb36..f89fd692e812 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -166,6 +166,8 @@ struct kobj_attribute { char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); + bool(*show_file_capable)(const struct file *); + bool(*store_file_capable)(const struct file *); }; extern const struct sysfs_ops kobj_sysfs_ops; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 786816cf4aa5..0b107795bee4 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -214,6 +214,8 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size) struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *, char *); ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); + bool(*show_file_capable)(const struct file *, struct attribute *); + bool(*store_file_capable)(const struct file *, struct attribute *); }; #ifdef CONFIG_SYSFS diff --git a/lib/kobject.c b/lib/kobject.c index b72e00fd7d09..81699b2b7f72 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -800,6 +800,18 @@ static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, return ret; } +static bool kobj_attr_show_file_capable(const struct file *file, + struct attribute *attr) +{ + struct kobj_attribute *kattr; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show_file_capable) + return kattr->show_file_capable(file); + + return true; +} + static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { @@ -812,9 +824,23 @@ static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, return ret; } +static bool kobj_attr_store_file_capable(const struct file *file, + struct attribute *attr) +{ + struct kobj_attribute *kattr; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store_file_capable) + return kattr->store_file_capable(file); + + return true; +} + const struct sysfs_ops kobj_sysfs_ops = { .show = kobj_attr_show, .store = kobj_attr_store, + .show_file_capable = kobj_attr_show_file_capable, + .store_file_capable = kobj_attr_store_file_capable, }; EXPORT_SYMBOL_GPL(kobj_sysfs_ops); -- 2.13.6
[PATCH 0/2] [RFC] sysfs: Add hook for checking the file capability of opener
There have some discussion in the following mail loop about checking capability in sysfs write handler: https://lkml.org/lkml/2018/9/13/978 Sometimes we check the capability in sysfs implementation by using capable function. But the checking can be bypassed by opening sysfs file within an unprivileged process then writing the file within a privileged process. The tricking way has been exposed by Andy Lutomirski for CVE-2013-1959. Because the sysfs_ops does not forward the file descriptor to the show/store callback, there doesn't have chance to check the capability of file's opener. This patch adds the hook to sysfs_ops that allows different implementation in object and attribute levels for checking file capable before accessing sysfs interfaces. The callback function of kobject sysfs_ops is the first implementation of new hook. It casts attribute to kobj_attribute then calls the file capability callback function of attribute level. The same logic can be implemented in other sysfs file types, like: device, driver and bus type. The capability checking logic in wake_lock/wake_unlock sysfs interface is the first example for kobject. It will check the opener's capability. Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Cc: Chen Yu Cc: Giovanni Gherdovich Cc: Jann Horn Cc: Andy Lutomirski Cc: Pavel Machek Cc: Len Brown Cc: "Martin K. Petersen" Cc: Randy Dunlap Cc: Joe Perches Cc: Bart Van Assche Signed-off-by: "Lee, Chun-Yi" Lee, Chun-Yi (2): sysfs: Add hook for checking the file capable PM / Sleep: Checking the file capability when writing wak lock interface fs/sysfs/file.c | 8 include/linux/kobject.h | 2 ++ include/linux/sysfs.h | 2 ++ kernel/power/main.c | 14 ++ kernel/power/wakelock.c | 6 -- lib/kobject.c | 26 ++ 6 files changed, 52 insertions(+), 6 deletions(-) -- 2.13.6
[PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image
When producing memory snapshot image, hibernation uses HMAC-SHA512 with snapshot key (from TPM trusted key) to calculate the hash of all data pages in snapshot image. The hash result will be kept in the snapshot header as the image signature. Before hibernation restores image, kernel executes HMAC-SHA512 again and compares the result with the signature in the header to verify the integrity of snapshot image. If the verification failed, the resume process will be stopped. Then the snapshot image will be discarded and system will boot as normal. On the other hand, a trampoline page be created in snapshot image when hibernation. This trampoline page be used to forward the state of snapshot key and the result of snapshot image verification from boot kernel to image kernel when resuming. The trampoline page will also be used to forward the snapshot key in the later patch. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 18 ++- kernel/power/power.h | 26 kernel/power/snapshot.c | 387 +-- kernel/power/swap.c | 6 + kernel/power/user.c | 12 ++ 5 files changed, 432 insertions(+), 17 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 18d13cbf0591..871a05e4467c 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -271,10 +271,14 @@ static int create_image(int platform_mode) { int error; + error = snapshot_prepare_hash(false); + if (error) + return error; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - return error; + goto finish_hash; } error = platform_pre_snapshot(platform_mode); @@ -331,6 +335,9 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_hash: + snapshot_finish_hash(); + return error; } @@ -694,6 +701,14 @@ int hibernate(void) return -EPERM; } + error = snapshot_key_init(); + if (error) + return error; + + error = snapshot_create_trampoline(); + if (error) + return error; + lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(_device_available, -1, 0)) { @@ -750,6 +765,7 @@ int hibernate(void) pm_restore_gfp_mask(); } else { pm_pr_dbg("Image restored successfully.\n"); + snapshot_restore_trampoline(); } Free_bitmaps: diff --git a/kernel/power/power.h b/kernel/power/power.h index fe2dfa0d4d36..c614b0a294e3 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -11,6 +11,10 @@ #define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE #define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE +/* HMAC algorithm for hibernate snapshot signature */ +#define SNAPSHOT_HMAC "hmac(sha512)" +#define SNAPSHOT_DIGEST_SIZE SHA512_DIGEST_SIZE + struct swsusp_info { struct new_utsname uts; u32 version_code; @@ -19,6 +23,17 @@ struct swsusp_info { unsigned long image_pages; unsigned long pages; unsigned long size; + unsigned long trampoline_pfn; + u8 signature[SNAPSHOT_DIGEST_SIZE]; +} __aligned(PAGE_SIZE); + +/* + * The trampoline page is used to forward information + * from boot kernel to image kernel in restore stage. + */ +struct trampoline { + bool snapshot_key_valid; + int sig_verify_ret; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -27,12 +42,19 @@ extern void __init hibernate_reserved_size_init(void); extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH +/* kernel/power/snapshot.c */ +extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_hash(bool may_sleep); +extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else +static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } +static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ @@ -171,6 +193,10 @@ extern int snapshot_read_next(struct snapshot_handle *handle); extern int snapshot_write_
[PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image
When producing memory snapshot image, hibernation uses HMAC-SHA512 with snapshot key (from TPM trusted key) to calculate the hash of all data pages in snapshot image. The hash result will be kept in the snapshot header as the image signature. Before hibernation restores image, kernel executes HMAC-SHA512 again and compares the result with the signature in the header to verify the integrity of snapshot image. If the verification failed, the resume process will be stopped. Then the snapshot image will be discarded and system will boot as normal. On the other hand, a trampoline page be created in snapshot image when hibernation. This trampoline page be used to forward the state of snapshot key and the result of snapshot image verification from boot kernel to image kernel when resuming. The trampoline page will also be used to forward the snapshot key in the later patch. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 18 ++- kernel/power/power.h | 26 kernel/power/snapshot.c | 387 +-- kernel/power/swap.c | 6 + kernel/power/user.c | 12 ++ 5 files changed, 432 insertions(+), 17 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 18d13cbf0591..871a05e4467c 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -271,10 +271,14 @@ static int create_image(int platform_mode) { int error; + error = snapshot_prepare_hash(false); + if (error) + return error; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - return error; + goto finish_hash; } error = platform_pre_snapshot(platform_mode); @@ -331,6 +335,9 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_hash: + snapshot_finish_hash(); + return error; } @@ -694,6 +701,14 @@ int hibernate(void) return -EPERM; } + error = snapshot_key_init(); + if (error) + return error; + + error = snapshot_create_trampoline(); + if (error) + return error; + lock_system_sleep(); /* The snapshot device should not be opened while we're running */ if (!atomic_add_unless(_device_available, -1, 0)) { @@ -750,6 +765,7 @@ int hibernate(void) pm_restore_gfp_mask(); } else { pm_pr_dbg("Image restored successfully.\n"); + snapshot_restore_trampoline(); } Free_bitmaps: diff --git a/kernel/power/power.h b/kernel/power/power.h index fe2dfa0d4d36..c614b0a294e3 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -11,6 +11,10 @@ #define SNAPSHOT_KEY_SIZE SHA512_DIGEST_SIZE #define DERIVED_KEY_SIZE SHA512_DIGEST_SIZE +/* HMAC algorithm for hibernate snapshot signature */ +#define SNAPSHOT_HMAC "hmac(sha512)" +#define SNAPSHOT_DIGEST_SIZE SHA512_DIGEST_SIZE + struct swsusp_info { struct new_utsname uts; u32 version_code; @@ -19,6 +23,17 @@ struct swsusp_info { unsigned long image_pages; unsigned long pages; unsigned long size; + unsigned long trampoline_pfn; + u8 signature[SNAPSHOT_DIGEST_SIZE]; +} __aligned(PAGE_SIZE); + +/* + * The trampoline page is used to forward information + * from boot kernel to image kernel in restore stage. + */ +struct trampoline { + bool snapshot_key_valid; + int sig_verify_ret; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -27,12 +42,19 @@ extern void __init hibernate_reserved_size_init(void); extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH +/* kernel/power/snapshot.c */ +extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_hash(bool may_sleep); +extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else +static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } +static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ @@ -171,6 +193,10 @@ extern int snapshot_read_next(struct snapshot_handle *handle); extern int snapshot_write_
[PATCH 5/5] PM / hibernate: An option to request that snapshot image must be authenticated
This kernel option is similar to the option for kernel module signature verification. When this option is unselected, kernel will be tainted by restored from a snapshot image without (valid) signature. When the option is selected, kernel will refuse the system to be restored from a unauthenticated image. The hibernation resume process will be stopped , the snapshot image will be discarded and system just boots as normal. The hibernation can be triggered without snapshot master key when this option is unselected. But kernel will be tainted after hibernation resume. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/kernel-parameters.txt | 6 include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 11 +++ kernel/power/hibernate.c| 8 +++-- kernel/power/power.h| 5 kernel/power/snapshot.c | 40 +++-- kernel/power/user.c | 2 +- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 970d837bd57f..c4be29103865 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3941,6 +3941,12 @@ protect_image Turn on image protection during restoration (that will set all pages holding image data during restoration read-only). + enforce_authWhen HIBERNATION_ENC_AUTH is set, this means + that snapshot image without (valid) signature + will fail to restore. + Note that if HIBERNATION_ENC_AUTH_FORCE is + set, that is always true, so this option does + nothing. retain_initrd [RAM] Keep initrd memory after extraction diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d6aac75b51ba..61714489cb57 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -598,7 +598,8 @@ extern enum system_states { #define TAINT_LIVEPATCH15 #define TAINT_AUX 16 #define TAINT_RANDSTRUCT 17 -#define TAINT_FLAGS_COUNT 18 +#define TAINT_UNSAFE_HIBERNATE 18 +#define TAINT_FLAGS_COUNT 19 struct taint_flag { char c_true;/* character printed when tainted */ diff --git a/kernel/panic.c b/kernel/panic.c index 8b2e002d52eb..624eb1150361 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -327,6 +327,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { [ TAINT_LIVEPATCH ] = { 'K', ' ', true }, [ TAINT_AUX ] = { 'X', ' ', true }, [ TAINT_RANDSTRUCT ]= { 'T', ' ', true }, + [ TAINT_UNSAFE_HIBERNATE ] = { 'H', ' ', true }, }; /** diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 7c5c30149dbc..3c998fd6dc4c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -90,6 +90,17 @@ config HIBERNATION_ENC_AUTH master key of hibernation. The TPM trusted key depends on TPM. The security of user defined key relies on user space. +config HIBERNATION_ENC_AUTH_FORCE + bool "Require hibernate snapshot image to be validly signed" + depends on HIBERNATION_ENC_AUTH + help + This option will prevent that a snapshot image without (valid) + signature be restored. Without this option, a unauthenticated + snapshot image can be restored but the restored kernel will be + tainted. Which also means that the hibernation can be triggered + without snapshot key but kernel will be tainted without this + option. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 79f4db284126..70a0d630a6c2 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -272,11 +272,11 @@ static int create_image(int platform_mode) int error; error = snapshot_prepare_hash(false); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_prepare_crypto(false, true); - if (error) + if (error && snapshot_is_enforce_auth()) goto finish_hash; error = dpm_suspend_end(PMSG_FREEZE); @@ -708,7 +708,7 @@ int hibernate(void) } error = snapshot_key_init(); - if (error) + if (error && sn
[PATCH 5/5] PM / hibernate: An option to request that snapshot image must be authenticated
This kernel option is similar to the option for kernel module signature verification. When this option is unselected, kernel will be tainted by restored from a snapshot image without (valid) signature. When the option is selected, kernel will refuse the system to be restored from a unauthenticated image. The hibernation resume process will be stopped , the snapshot image will be discarded and system just boots as normal. The hibernation can be triggered without snapshot master key when this option is unselected. But kernel will be tainted after hibernation resume. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- Documentation/admin-guide/kernel-parameters.txt | 6 include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 11 +++ kernel/power/hibernate.c| 8 +++-- kernel/power/power.h| 5 kernel/power/snapshot.c | 40 +++-- kernel/power/user.c | 2 +- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 970d837bd57f..c4be29103865 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3941,6 +3941,12 @@ protect_image Turn on image protection during restoration (that will set all pages holding image data during restoration read-only). + enforce_authWhen HIBERNATION_ENC_AUTH is set, this means + that snapshot image without (valid) signature + will fail to restore. + Note that if HIBERNATION_ENC_AUTH_FORCE is + set, that is always true, so this option does + nothing. retain_initrd [RAM] Keep initrd memory after extraction diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d6aac75b51ba..61714489cb57 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -598,7 +598,8 @@ extern enum system_states { #define TAINT_LIVEPATCH15 #define TAINT_AUX 16 #define TAINT_RANDSTRUCT 17 -#define TAINT_FLAGS_COUNT 18 +#define TAINT_UNSAFE_HIBERNATE 18 +#define TAINT_FLAGS_COUNT 19 struct taint_flag { char c_true;/* character printed when tainted */ diff --git a/kernel/panic.c b/kernel/panic.c index 8b2e002d52eb..624eb1150361 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -327,6 +327,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { [ TAINT_LIVEPATCH ] = { 'K', ' ', true }, [ TAINT_AUX ] = { 'X', ' ', true }, [ TAINT_RANDSTRUCT ]= { 'T', ' ', true }, + [ TAINT_UNSAFE_HIBERNATE ] = { 'H', ' ', true }, }; /** diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 7c5c30149dbc..3c998fd6dc4c 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -90,6 +90,17 @@ config HIBERNATION_ENC_AUTH master key of hibernation. The TPM trusted key depends on TPM. The security of user defined key relies on user space. +config HIBERNATION_ENC_AUTH_FORCE + bool "Require hibernate snapshot image to be validly signed" + depends on HIBERNATION_ENC_AUTH + help + This option will prevent that a snapshot image without (valid) + signature be restored. Without this option, a unauthenticated + snapshot image can be restored but the restored kernel will be + tainted. Which also means that the hibernation can be triggered + without snapshot key but kernel will be tainted without this + option. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 79f4db284126..70a0d630a6c2 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -272,11 +272,11 @@ static int create_image(int platform_mode) int error; error = snapshot_prepare_hash(false); - if (error) + if (error && snapshot_is_enforce_auth()) return error; error = snapshot_prepare_crypto(false, true); - if (error) + if (error && snapshot_is_enforce_auth()) goto finish_hash; error = dpm_suspend_end(PMSG_FREEZE); @@ -708,7 +708,7 @@ int hibernate(void) } error = snapshot_key_init(); - if (error) + if (error && sn
[PATCH 4/5] PM / hibernate: Erase the snapshot master key in snapshot pages
If the encryption key be guessed then the snapshot master key can also be grabbed from snapshot image. Which means that the authentication key can also be calculated. So kernel erases master key in snapshot pages. Because the master key in image kernel be erased, kernel uses the trampoline page to forward snapshot master key to image kernel. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/power.h| 6 + kernel/power/snapshot.c | 5 kernel/power/snapshot_key.c | 66 + 3 files changed, 77 insertions(+) diff --git a/kernel/power/power.h b/kernel/power/power.h index 41263fdd3a54..d2fc73b2e200 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -36,6 +36,7 @@ struct swsusp_info { struct trampoline { bool snapshot_key_valid; int sig_verify_ret; + u8 snapshot_key[SNAPSHOT_KEY_SIZE]; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -55,6 +56,9 @@ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); +extern void snapshot_key_page_erase(unsigned long pfn, void *buff_addr); +extern void snapshot_key_trampoline_backup(struct trampoline *t); +extern void snapshot_key_trampoline_restore(struct trampoline *t); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } @@ -62,6 +66,8 @@ static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } +static inline void snapshot_key_trampoline_backup(struct trampoline *t) {} +static inline void snapshot_key_trampoline_restore(struct trampoline *t) {} #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ #ifdef CONFIG_ARCH_HIBERNATION_HEADER diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c9a6e4983571..dd176df75d2b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1696,6 +1696,9 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page); } + /* Erase key data in snapshot */ + snapshot_key_page_erase(pfn, crypto_buffer); + /* Encrypt hashed page */ encrypt_data_page(crypto_buffer); @@ -2481,6 +2484,7 @@ void snapshot_init_trampoline(void) t = (struct trampoline *)trampoline_buff; init_sig_verify(t); + snapshot_key_trampoline_backup(t); pr_info("Hibernation trampoline page prepared\n"); } @@ -2504,6 +2508,7 @@ void snapshot_restore_trampoline(void) t = (struct trampoline *)trampoline_virt; handle_sig_verify(t); + snapshot_key_trampoline_restore(t); snapshot_free_trampoline(); } diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c index 091f33929b47..c0f179283f9e 100644 --- a/kernel/power/snapshot_key.c +++ b/kernel/power/snapshot_key.c @@ -29,11 +29,26 @@ static struct snapshot_key { const char *key_name; bool initialized; unsigned int key_len; + unsigned long pfn; /* pfn of keyblob */ + unsigned long addr_offset; /* offset in page for keyblob */ u8 key[SNAPSHOT_KEY_SIZE]; + u8 fingerprint[SHA512_DIGEST_SIZE]; /* fingerprint of keyblob */ } skey = { .key_name = "swsusp-kmk", }; +static void snapshot_key_clean(void) +{ + crypto_free_shash(hash_tfm); + hash_tfm = NULL; + skey.pfn = 0; + skey.key_len = 0; + skey.addr_offset = 0; + memzero_explicit(skey.key, SNAPSHOT_KEY_SIZE); + memzero_explicit(skey.fingerprint, SHA512_DIGEST_SIZE); + skey.initialized = false; +} + static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, bool may_sleep) { @@ -74,6 +89,53 @@ static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, return ret; } +static int get_key_fingerprint(u8 *fingerprint, u8 *key, unsigned int key_len, + bool may_sleep) +{ + return calc_key_hash(key, key_len, "FINGERPRINT", fingerprint, may_sleep); +} + +void snapshot_key_page_erase(unsigned long pfn, void *buff_addr) +{ + if (!skey.initialized || pfn != skey.pfn) + return; + + /* erase key data from snapshot buffer page */ + if (!memcmp(skey.key, buff_addr + skey.addr_offset, skey.key_len)) { + memzero_explicit(buff_addr + skey.addr_offs
[PATCH 4/5] PM / hibernate: Erase the snapshot master key in snapshot pages
If the encryption key be guessed then the snapshot master key can also be grabbed from snapshot image. Which means that the authentication key can also be calculated. So kernel erases master key in snapshot pages. Because the master key in image kernel be erased, kernel uses the trampoline page to forward snapshot master key to image kernel. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/power.h| 6 + kernel/power/snapshot.c | 5 kernel/power/snapshot_key.c | 66 + 3 files changed, 77 insertions(+) diff --git a/kernel/power/power.h b/kernel/power/power.h index 41263fdd3a54..d2fc73b2e200 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -36,6 +36,7 @@ struct swsusp_info { struct trampoline { bool snapshot_key_valid; int sig_verify_ret; + u8 snapshot_key[SNAPSHOT_KEY_SIZE]; } __aligned(PAGE_SIZE); #ifdef CONFIG_HIBERNATION @@ -55,6 +56,9 @@ extern int snapshot_key_init(void); extern bool snapshot_key_initialized(void); extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); +extern void snapshot_key_page_erase(unsigned long pfn, void *buff_addr); +extern void snapshot_key_trampoline_backup(struct trampoline *t); +extern void snapshot_key_trampoline_restore(struct trampoline *t); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } @@ -62,6 +66,8 @@ static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } +static inline void snapshot_key_trampoline_backup(struct trampoline *t) {} +static inline void snapshot_key_trampoline_restore(struct trampoline *t) {} #endif /* !CONFIG_HIBERNATION_ENC_AUTH */ #ifdef CONFIG_ARCH_HIBERNATION_HEADER diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index c9a6e4983571..dd176df75d2b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1696,6 +1696,9 @@ __copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm) crypto_buffer = page_address(d_page); } + /* Erase key data in snapshot */ + snapshot_key_page_erase(pfn, crypto_buffer); + /* Encrypt hashed page */ encrypt_data_page(crypto_buffer); @@ -2481,6 +2484,7 @@ void snapshot_init_trampoline(void) t = (struct trampoline *)trampoline_buff; init_sig_verify(t); + snapshot_key_trampoline_backup(t); pr_info("Hibernation trampoline page prepared\n"); } @@ -2504,6 +2508,7 @@ void snapshot_restore_trampoline(void) t = (struct trampoline *)trampoline_virt; handle_sig_verify(t); + snapshot_key_trampoline_restore(t); snapshot_free_trampoline(); } diff --git a/kernel/power/snapshot_key.c b/kernel/power/snapshot_key.c index 091f33929b47..c0f179283f9e 100644 --- a/kernel/power/snapshot_key.c +++ b/kernel/power/snapshot_key.c @@ -29,11 +29,26 @@ static struct snapshot_key { const char *key_name; bool initialized; unsigned int key_len; + unsigned long pfn; /* pfn of keyblob */ + unsigned long addr_offset; /* offset in page for keyblob */ u8 key[SNAPSHOT_KEY_SIZE]; + u8 fingerprint[SHA512_DIGEST_SIZE]; /* fingerprint of keyblob */ } skey = { .key_name = "swsusp-kmk", }; +static void snapshot_key_clean(void) +{ + crypto_free_shash(hash_tfm); + hash_tfm = NULL; + skey.pfn = 0; + skey.key_len = 0; + skey.addr_offset = 0; + memzero_explicit(skey.key, SNAPSHOT_KEY_SIZE); + memzero_explicit(skey.fingerprint, SHA512_DIGEST_SIZE); + skey.initialized = false; +} + static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen, bool may_sleep) { @@ -74,6 +89,53 @@ static int calc_key_hash(u8 *key, unsigned int key_len, const char *salt, return ret; } +static int get_key_fingerprint(u8 *fingerprint, u8 *key, unsigned int key_len, + bool may_sleep) +{ + return calc_key_hash(key, key_len, "FINGERPRINT", fingerprint, may_sleep); +} + +void snapshot_key_page_erase(unsigned long pfn, void *buff_addr) +{ + if (!skey.initialized || pfn != skey.pfn) + return; + + /* erase key data from snapshot buffer page */ + if (!memcmp(skey.key, buff_addr + skey.addr_offset, skey.key_len)) { + memzero_explicit(buff_addr + skey.addr_offs
[PATCH 3/5] PM / hibernate: Encrypt snapshot image
To protect the secret in memory snapshot image, this patch adds the logic to encrypt snapshot pages by AES-CTR. Using AES-CTR is because it's simple, fast and parallelizable. But this patch didn't implement parallel encryption. The encrypt key is derived from the snapshot key. And the initialization vector will be kept in snapshot header for resuming. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 8 ++- kernel/power/power.h | 6 ++ kernel/power/snapshot.c | 154 ++- 3 files changed, 164 insertions(+), 4 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 871a05e4467c..79f4db284126 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -275,10 +275,14 @@ static int create_image(int platform_mode) if (error) return error; + error = snapshot_prepare_crypto(false, true); + if (error) + goto finish_hash; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - goto finish_hash; + goto finish_crypto; } error = platform_pre_snapshot(platform_mode); @@ -335,6 +339,8 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_crypto: + snapshot_finish_crypto(); finish_hash: snapshot_finish_hash(); diff --git a/kernel/power/power.h b/kernel/power/power.h index c614b0a294e3..41263fdd3a54 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -5,6 +5,7 @@ #include #include #include +#include /* The max size of encrypted key blob */ #define KEY_BLOB_BUFF_LEN 512 @@ -24,6 +25,7 @@ struct swsusp_info { unsigned long pages; unsigned long size; unsigned long trampoline_pfn; + u8 iv[AES_BLOCK_SIZE]; u8 signature[SNAPSHOT_DIGEST_SIZE]; } __aligned(PAGE_SIZE); @@ -44,6 +46,8 @@ extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH /* kernel/power/snapshot.c */ extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv); +extern void snapshot_finish_crypto(void); extern int snapshot_prepare_hash(bool may_sleep); extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ @@ -53,6 +57,8 @@ extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } +static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 949542ed5ffe..c9a6e4983571 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -41,7 +41,11 @@ #include #include #ifdef CONFIG_HIBERNATION_ENC_AUTH +#include +#include +#include #include +#include #endif #include "power.h" @@ -1412,6 +1416,127 @@ static unsigned int nr_copy_pages; static void **h_buf; #ifdef CONFIG_HIBERNATION_ENC_AUTH +static struct skcipher_request *sk_req; +static u8 iv[AES_BLOCK_SIZE]; +static void *c_buffer; + +static void init_iv(struct swsusp_info *info) +{ + memcpy(info->iv, iv, AES_BLOCK_SIZE); +} + +static void load_iv(struct swsusp_info *info) +{ + memcpy(iv, info->iv, AES_BLOCK_SIZE); +} + +int snapshot_prepare_crypto(bool may_sleep, bool create_iv) +{ + char enc_key[DERIVED_KEY_SIZE]; + struct crypto_skcipher *tfm; + int ret = 0; + + ret = snapshot_get_enc_key(enc_key, may_sleep); + if (ret) { + pr_warn_once("enc key is invalid\n"); + return -EINVAL; + } + + c_buffer = (void *)get_zeroed_page(GFP_KERNEL); + if (!c_buffer) { + pr_err("Allocate crypto buffer page failed\n"); + return -ENOMEM; + } + + tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + pr_err("failed to allocate skcipher (%d)\n", ret); + goto alloc_fail; + } + + ret = crypto_skcipher_setkey(tfm, enc_key, AES_MAX_KEY_SIZE); + if (ret) { + pr_err("failed to setkey (%d)\n&quo
[PATCH 3/5] PM / hibernate: Encrypt snapshot image
To protect the secret in memory snapshot image, this patch adds the logic to encrypt snapshot pages by AES-CTR. Using AES-CTR is because it's simple, fast and parallelizable. But this patch didn't implement parallel encryption. The encrypt key is derived from the snapshot key. And the initialization vector will be kept in snapshot header for resuming. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/hibernate.c | 8 ++- kernel/power/power.h | 6 ++ kernel/power/snapshot.c | 154 ++- 3 files changed, 164 insertions(+), 4 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 871a05e4467c..79f4db284126 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -275,10 +275,14 @@ static int create_image(int platform_mode) if (error) return error; + error = snapshot_prepare_crypto(false, true); + if (error) + goto finish_hash; + error = dpm_suspend_end(PMSG_FREEZE); if (error) { pr_err("Some devices failed to power down, aborting hibernation\n"); - goto finish_hash; + goto finish_crypto; } error = platform_pre_snapshot(platform_mode); @@ -335,6 +339,8 @@ static int create_image(int platform_mode) dpm_resume_start(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); + finish_crypto: + snapshot_finish_crypto(); finish_hash: snapshot_finish_hash(); diff --git a/kernel/power/power.h b/kernel/power/power.h index c614b0a294e3..41263fdd3a54 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -5,6 +5,7 @@ #include #include #include +#include /* The max size of encrypted key blob */ #define KEY_BLOB_BUFF_LEN 512 @@ -24,6 +25,7 @@ struct swsusp_info { unsigned long pages; unsigned long size; unsigned long trampoline_pfn; + u8 iv[AES_BLOCK_SIZE]; u8 signature[SNAPSHOT_DIGEST_SIZE]; } __aligned(PAGE_SIZE); @@ -44,6 +46,8 @@ extern void __init hibernate_image_size_init(void); #ifdef CONFIG_HIBERNATION_ENC_AUTH /* kernel/power/snapshot.c */ extern int snapshot_image_verify_decrypt(void); +extern int snapshot_prepare_crypto(bool may_sleep, bool create_iv); +extern void snapshot_finish_crypto(void); extern int snapshot_prepare_hash(bool may_sleep); extern void snapshot_finish_hash(void); /* kernel/power/snapshot_key.c */ @@ -53,6 +57,8 @@ extern int snapshot_get_auth_key(u8 *auth_key, bool may_sleep); extern int snapshot_get_enc_key(u8 *enc_key, bool may_sleep); #else static inline int snapshot_image_verify_decrypt(void) { return 0; } +static inline int snapshot_prepare_crypto(bool may_sleep, bool create_iv) { return 0; } +static inline void snapshot_finish_crypto(void) {} static inline int snapshot_prepare_hash(bool may_sleep) { return 0; } static inline void snapshot_finish_hash(void) {} static inline int snapshot_key_init(void) { return 0; } diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 949542ed5ffe..c9a6e4983571 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -41,7 +41,11 @@ #include #include #ifdef CONFIG_HIBERNATION_ENC_AUTH +#include +#include +#include #include +#include #endif #include "power.h" @@ -1412,6 +1416,127 @@ static unsigned int nr_copy_pages; static void **h_buf; #ifdef CONFIG_HIBERNATION_ENC_AUTH +static struct skcipher_request *sk_req; +static u8 iv[AES_BLOCK_SIZE]; +static void *c_buffer; + +static void init_iv(struct swsusp_info *info) +{ + memcpy(info->iv, iv, AES_BLOCK_SIZE); +} + +static void load_iv(struct swsusp_info *info) +{ + memcpy(iv, info->iv, AES_BLOCK_SIZE); +} + +int snapshot_prepare_crypto(bool may_sleep, bool create_iv) +{ + char enc_key[DERIVED_KEY_SIZE]; + struct crypto_skcipher *tfm; + int ret = 0; + + ret = snapshot_get_enc_key(enc_key, may_sleep); + if (ret) { + pr_warn_once("enc key is invalid\n"); + return -EINVAL; + } + + c_buffer = (void *)get_zeroed_page(GFP_KERNEL); + if (!c_buffer) { + pr_err("Allocate crypto buffer page failed\n"); + return -ENOMEM; + } + + tfm = crypto_alloc_skcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + pr_err("failed to allocate skcipher (%d)\n", ret); + goto alloc_fail; + } + + ret = crypto_skcipher_setkey(tfm, enc_key, AES_MAX_KEY_SIZE); + if (ret) { + pr_err("failed to setkey (%d)\n&quo
[PATCH 0/5][RFC] Encryption and authentication for hibernate snapshot image
Hi, This patchset is the implementation of encryption and authentication for hibernate snapshot image. The image will be encrypted by AES and authenticated by HMAC. The hibernate function can be used to snapshot memory pages to an image, then kernel restores the image to memory space in a appropriate time. There have secrets in snapshot image and cracker may modifies it for hacking system. Encryption and authentication of snapshot image can protect the system. Hibernate function requests the master key through key retention service. The snapshot master key can be a trusted key or a user defined key. The name of snapshot master key is fixed to "swsusp-kmk". User should loads swsusp-kmk to kernel by keyctl tool before the hibernation resume. e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume The TPM trusted key type is preferred to be the master key. But user defined key can also be used for testing or when the platform doesn't have TPM. User must be aware that the security of user key relies on user space. If the root account be compromised, then the user key will easy to be grabbed. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" Lee, Chun-Yi (5): PM / hibernate: Create snapshot keys handler PM / hibernate: Generate and verify signature for snapshot image PM / hibernate: Encrypt snapshot image PM / hibernate: Erase the snapshot master key in snapshot pages PM / hibernate: An option to request that snapshot image must be authenticated Documentation/admin-guide/kernel-parameters.txt | 6 + include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 25 + kernel/power/Makefile | 1 + kernel/power/hibernate.c| 62 ++- kernel/power/power.h| 59 +++ kernel/power/snapshot.c | 576 +++- kernel/power/snapshot_key.c | 303 + kernel/power/swap.c | 6 + kernel/power/user.c | 12 + 11 files changed, 1036 insertions(+), 18 deletions(-) create mode 100644 kernel/power/snapshot_key.c -- 2.13.6
[PATCH 0/5][RFC] Encryption and authentication for hibernate snapshot image
Hi, This patchset is the implementation of encryption and authentication for hibernate snapshot image. The image will be encrypted by AES and authenticated by HMAC. The hibernate function can be used to snapshot memory pages to an image, then kernel restores the image to memory space in a appropriate time. There have secrets in snapshot image and cracker may modifies it for hacking system. Encryption and authentication of snapshot image can protect the system. Hibernate function requests the master key through key retention service. The snapshot master key can be a trusted key or a user defined key. The name of snapshot master key is fixed to "swsusp-kmk". User should loads swsusp-kmk to kernel by keyctl tool before the hibernation resume. e.g. The swsusp-kmk must be loaded before systemd-hibernate-resume The TPM trusted key type is preferred to be the master key. But user defined key can also be used for testing or when the platform doesn't have TPM. User must be aware that the security of user key relies on user space. If the root account be compromised, then the user key will easy to be grabbed. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" Lee, Chun-Yi (5): PM / hibernate: Create snapshot keys handler PM / hibernate: Generate and verify signature for snapshot image PM / hibernate: Encrypt snapshot image PM / hibernate: Erase the snapshot master key in snapshot pages PM / hibernate: An option to request that snapshot image must be authenticated Documentation/admin-guide/kernel-parameters.txt | 6 + include/linux/kernel.h | 3 +- kernel/panic.c | 1 + kernel/power/Kconfig| 25 + kernel/power/Makefile | 1 + kernel/power/hibernate.c| 62 ++- kernel/power/power.h| 59 +++ kernel/power/snapshot.c | 576 +++- kernel/power/snapshot_key.c | 303 + kernel/power/swap.c | 6 + kernel/power/user.c | 12 + 11 files changed, 1036 insertions(+), 18 deletions(-) create mode 100644 kernel/power/snapshot_key.c -- 2.13.6
[PATCH 1/5] PM / hibernate: Create snapshot keys handler
This patch adds a snapshot keys handler for using the key retention service api to create keys for snapshot image encryption and authentication. This handler uses TPM trusted key as the snapshot master key, and the encryption key and authentication key are derived from the snapshot key. The user defined key can also be used as the snapshot master key , but user must be aware that the security of user key relies on user space. The name of snapshot key is fixed to "swsusp-kmk". User should use the keyctl tool to load the key blob to root's user keyring. e.g. # /bin/keyctl add trusted swsusp-kmk "load `cat swsusp-kmk.blob`" @u or create a new user key. e.g. # /bin/keyctl add user swsusp-kmk password @u Then the disk_kmk sysfs file can be used to trigger the initialization of snapshot key: # echo 1 > /sys/power/disk_kmk After the initialization be triggered, the secret in the payload of swsusp-key will be copied by hibernation and be erased. Then user can use keyctl to remove swsusp-kmk key from root's keyring. If user does not trigger the initialization by disk_kmk file after swsusp-kmk be loaded to kernel. Then the snapshot key will be initialled when hibernation be triggered. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/Kconfig| 14 +++ kernel/power/Makefile | 1 + kernel/power/hibernate.c| 36 +++ kernel/power/power.h| 16 +++ kernel/power/snapshot_key.c | 237 5 files changed, 304 insertions(+) create mode 100644 kernel/power/snapshot_key.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3a6c2f87699e..7c5c30149dbc 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -76,6 +76,20 @@ config HIBERNATION For more information take a look at . +config HIBERNATION_ENC_AUTH + bool "Hibernation encryption and authentication" + depends on HIBERNATION + depends on TRUSTED_KEYS + select CRYPTO_AES + select CRYPTO_HMAC + select CRYPTO_SHA512 + help + This option will encrypt and authenticate the memory snapshot image + of hibernation. It prevents that the snapshot image be arbitrary + modified. User can use TPMs trusted key or user defined key as the + master key of hibernation. The TPM trusted key depends on TPM. The + security of user defined key relies on user space. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index a3f79f0eef36..bddca7b79a28 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o +obj-$(CONFIG_HIBERNATION_ENC_AUTH) += snapshot_key.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index abef759de7c8..18d13cbf0591 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1034,6 +1034,39 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(disk); +#ifdef CONFIG_HIBERNATION_ENC_AUTH +static ssize_t disk_kmk_show(struct kobject *kobj, struct kobj_attribute *attr, +char *buf) +{ + if (snapshot_key_initialized()) + return sprintf(buf, "initialized\n"); + else + return sprintf(buf, "uninitialized\n"); +} + +static ssize_t disk_kmk_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = 0; + char *p; + int len; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + if (strncmp(buf, "1", len)) + return -EINVAL; + + error = snapshot_key_init(); + + return error ? error : n; +} + +power_attr(disk_kmk); +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1138,6 +1171,9 @@ power_attr(reserved_size); static struct attribute * g[] = { _attr.attr, +#ifdef CONFIG_HIBERNATION_ENC_AUTH + _kmk_attr.attr, +#endif _offset_attr.attr, _attr.attr, _size_attr.attr, diff --git a/kernel/power/power.h b/kernel/power/power.h index 9e58bdc8a562..fe2dfa0d4d36 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -4,6 +4,12 @@ #include #include #include +#include + +/* The max size
[PATCH 1/5] PM / hibernate: Create snapshot keys handler
This patch adds a snapshot keys handler for using the key retention service api to create keys for snapshot image encryption and authentication. This handler uses TPM trusted key as the snapshot master key, and the encryption key and authentication key are derived from the snapshot key. The user defined key can also be used as the snapshot master key , but user must be aware that the security of user key relies on user space. The name of snapshot key is fixed to "swsusp-kmk". User should use the keyctl tool to load the key blob to root's user keyring. e.g. # /bin/keyctl add trusted swsusp-kmk "load `cat swsusp-kmk.blob`" @u or create a new user key. e.g. # /bin/keyctl add user swsusp-kmk password @u Then the disk_kmk sysfs file can be used to trigger the initialization of snapshot key: # echo 1 > /sys/power/disk_kmk After the initialization be triggered, the secret in the payload of swsusp-key will be copied by hibernation and be erased. Then user can use keyctl to remove swsusp-kmk key from root's keyring. If user does not trigger the initialization by disk_kmk file after swsusp-kmk be loaded to kernel. Then the snapshot key will be initialled when hibernation be triggered. Cc: "Rafael J. Wysocki" Cc: Pavel Machek Cc: Chen Yu Cc: Oliver Neukum Cc: Ryan Chen Cc: David Howells Cc: Giovanni Gherdovich Signed-off-by: "Lee, Chun-Yi" --- kernel/power/Kconfig| 14 +++ kernel/power/Makefile | 1 + kernel/power/hibernate.c| 36 +++ kernel/power/power.h| 16 +++ kernel/power/snapshot_key.c | 237 5 files changed, 304 insertions(+) create mode 100644 kernel/power/snapshot_key.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3a6c2f87699e..7c5c30149dbc 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -76,6 +76,20 @@ config HIBERNATION For more information take a look at . +config HIBERNATION_ENC_AUTH + bool "Hibernation encryption and authentication" + depends on HIBERNATION + depends on TRUSTED_KEYS + select CRYPTO_AES + select CRYPTO_HMAC + select CRYPTO_SHA512 + help + This option will encrypt and authenticate the memory snapshot image + of hibernation. It prevents that the snapshot image be arbitrary + modified. User can use TPMs trusted key or user defined key as the + master key of hibernation. The TPM trusted key depends on TPM. The + security of user defined key relies on user space. + config ARCH_SAVE_PAGE_KEYS bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index a3f79f0eef36..bddca7b79a28 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o +obj-$(CONFIG_HIBERNATION_ENC_AUTH) += snapshot_key.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index abef759de7c8..18d13cbf0591 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1034,6 +1034,39 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, power_attr(disk); +#ifdef CONFIG_HIBERNATION_ENC_AUTH +static ssize_t disk_kmk_show(struct kobject *kobj, struct kobj_attribute *attr, +char *buf) +{ + if (snapshot_key_initialized()) + return sprintf(buf, "initialized\n"); + else + return sprintf(buf, "uninitialized\n"); +} + +static ssize_t disk_kmk_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + int error = 0; + char *p; + int len; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + if (strncmp(buf, "1", len)) + return -EINVAL; + + error = snapshot_key_init(); + + return error ? error : n; +} + +power_attr(disk_kmk); +#endif /* !CONFIG_HIBERNATION_ENC_AUTH */ + static ssize_t resume_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1138,6 +1171,9 @@ power_attr(reserved_size); static struct attribute * g[] = { _attr.attr, +#ifdef CONFIG_HIBERNATION_ENC_AUTH + _kmk_attr.attr, +#endif _offset_attr.attr, _attr.attr, _size_attr.attr, diff --git a/kernel/power/power.h b/kernel/power/power.h index 9e58bdc8a562..fe2dfa0d4d36 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -4,6 +4,12 @@ #include #include #include +#include + +/* The max size
[PATCH] x86/PCI: Claim the resources of firmware enabled IOAPIC before children bus
I got a machine that the resource of firmware enabled IOAPIC conflicts with the resource of a children bus when the PCI host bus be hotplug. [ 3182.243325] PCI host bridge to bus 0001:40 [ 3182.243328] pci_bus 0001:40: root bus resource [io 0xc000-0xdfff window] [ 3182.243330] pci_bus 0001:40: root bus resource [mem 0xdc00-0xebff window] [ 3182.243331] pci_bus 0001:40: root bus resource [mem 0x2124-0x2125 window] [ 3182.243334] pci_bus 0001:40: root bus resource [bus 40-7e] ... [ 3182.244737] pci 0001:40:05.4: [8086:6f2c] type 00 class 0x080020 [ 3182.244746] pci 0001:40:05.4: reg 0x10: [mem 0xdc00-0xdc000fff] ... [ 3182.246697] pci 0001:40:02.0: PCI bridge to [bus 41] [ 3182.246702] pci 0001:40:02.0: bridge window [mem 0xdc00-0xdc7f] ... pci 0001:40:05.4: can't claim BAR 0 [mem 0xdc00-0xdc000fff]: address conflict with PCI Bus 0001:41 [mem 0xdc00-0xdc7f] The bus topology: +-[0001:40]-+-02.0-[41]-- | +-03.0-[41]-- | +-03.2-[41]--+-00.0 Intel Corporation I350 Gigabit Network Connection | |+-00.1 Intel Corporation I350 Gigabit Network Connection | |+-00.2 Intel Corporation I350 Gigabit Network Connection | |\-00.3 Intel Corporation I350 Gigabit Network Connection | +-05.0 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management | +-05.1 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug | +-05.2 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors | \-05.4 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC This problem causes that the NIC behine the child bus was not available after PCI host bridge hotpluged. Kernel does not want to change resource of firmware enabled IOAPIC, but the priority of children bus's resources are higher than any other devices. So this conflict can not be handled by the reassigning logic of kernel. This patch claims the resources of firmware enabled IOAPIC before children bus. Then kernel gets a chance to reassign the resources of children bus to avoid the conflict. Cc: Bjorn Helgaas Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Signed-off-by: "Lee, Chun-Yi" --- arch/x86/pci/i386.c | 27 +++ 1 file changed, 27 insertions(+) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index ed4ac215305d..6413eda87c72 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -230,13 +230,40 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev) } } +static bool ioapic_firmware_enabled(struct pci_dev *dev) +{ + u16 class = dev->class >> 8; + + if (class == PCI_CLASS_SYSTEM_PIC) { + u16 command; + + pci_read_config_word(dev, PCI_COMMAND, ); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + return true; + } + + return false; +} + +static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass); + static void pcibios_allocate_bus_resources(struct pci_bus *bus) { struct pci_bus *child; + struct pci_dev *dev; /* Depth-First Search on bus tree */ if (bus->self) pcibios_allocate_bridge_resources(bus->self); + + /* allocate firmware enabled APIC before children bus */ + list_for_each_entry(dev, >devices, bus_list) { + if (ioapic_firmware_enabled(dev)) { + pcibios_allocate_dev_resources(dev, 0); + pcibios_allocate_dev_resources(dev, 1); + } + } + list_for_each_entry(child, >children, node) pcibios_allocate_bus_resources(child); } -- 2.13.6
[PATCH] x86/PCI: Claim the resources of firmware enabled IOAPIC before children bus
I got a machine that the resource of firmware enabled IOAPIC conflicts with the resource of a children bus when the PCI host bus be hotplug. [ 3182.243325] PCI host bridge to bus 0001:40 [ 3182.243328] pci_bus 0001:40: root bus resource [io 0xc000-0xdfff window] [ 3182.243330] pci_bus 0001:40: root bus resource [mem 0xdc00-0xebff window] [ 3182.243331] pci_bus 0001:40: root bus resource [mem 0x2124-0x2125 window] [ 3182.243334] pci_bus 0001:40: root bus resource [bus 40-7e] ... [ 3182.244737] pci 0001:40:05.4: [8086:6f2c] type 00 class 0x080020 [ 3182.244746] pci 0001:40:05.4: reg 0x10: [mem 0xdc00-0xdc000fff] ... [ 3182.246697] pci 0001:40:02.0: PCI bridge to [bus 41] [ 3182.246702] pci 0001:40:02.0: bridge window [mem 0xdc00-0xdc7f] ... pci 0001:40:05.4: can't claim BAR 0 [mem 0xdc00-0xdc000fff]: address conflict with PCI Bus 0001:41 [mem 0xdc00-0xdc7f] The bus topology: +-[0001:40]-+-02.0-[41]-- | +-03.0-[41]-- | +-03.2-[41]--+-00.0 Intel Corporation I350 Gigabit Network Connection | |+-00.1 Intel Corporation I350 Gigabit Network Connection | |+-00.2 Intel Corporation I350 Gigabit Network Connection | |\-00.3 Intel Corporation I350 Gigabit Network Connection | +-05.0 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management | +-05.1 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug | +-05.2 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors | \-05.4 Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC This problem causes that the NIC behine the child bus was not available after PCI host bridge hotpluged. Kernel does not want to change resource of firmware enabled IOAPIC, but the priority of children bus's resources are higher than any other devices. So this conflict can not be handled by the reassigning logic of kernel. This patch claims the resources of firmware enabled IOAPIC before children bus. Then kernel gets a chance to reassign the resources of children bus to avoid the conflict. Cc: Bjorn Helgaas Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Signed-off-by: "Lee, Chun-Yi" --- arch/x86/pci/i386.c | 27 +++ 1 file changed, 27 insertions(+) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index ed4ac215305d..6413eda87c72 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -230,13 +230,40 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev) } } +static bool ioapic_firmware_enabled(struct pci_dev *dev) +{ + u16 class = dev->class >> 8; + + if (class == PCI_CLASS_SYSTEM_PIC) { + u16 command; + + pci_read_config_word(dev, PCI_COMMAND, ); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + return true; + } + + return false; +} + +static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass); + static void pcibios_allocate_bus_resources(struct pci_bus *bus) { struct pci_bus *child; + struct pci_dev *dev; /* Depth-First Search on bus tree */ if (bus->self) pcibios_allocate_bridge_resources(bus->self); + + /* allocate firmware enabled APIC before children bus */ + list_for_each_entry(dev, >devices, bus_list) { + if (ioapic_firmware_enabled(dev)) { + pcibios_allocate_dev_resources(dev, 0); + pcibios_allocate_dev_resources(dev, 1); + } + } + list_for_each_entry(child, >children, node) pcibios_allocate_bus_resources(child); } -- 2.13.6
[PATCH] efi: Fix the size not consistent issue when unmapping memory map
When using kdump, SOMETIMES the "size not consistent" warning message shows up when the crash kernel boots with early_ioremap_debug parameter: WARNING: CPU: 0 PID: 0 at ../mm/early_ioremap.c:182 early_iounmap+0x4f/0x12c() early_iounmap(ff200180, 0118) [0] size not consistent 0120 The root cause is that the unmapping size of memory map doesn't match with the original size when mapping: in __efi_memmap_init() map.map = early_memremap(phys_map, data->size); in efi_memmap_unmap() size = efi.memmap.desc_size * efi.memmap.nr_map; early_memunmap(efi.memmap.map, size); But the efi.memmap.nr_map is from __efi_memmap_init(). The remainder of size was discarded when calculating the nr_map: map.nr_map = data->size / data->desc_size; When the original size of memory map region does not equal to the result of multiplication. The "size not consistent" warning will be triggered. This issue sometimes was hit by kdump because kexec set the efi map size to align with 16 when loading crash kernel image: in bzImage64_load() efi_map_sz = efi_get_runtime_map_size(); efi_map_sz = ALIGN(efi_map_sz, 16); Dave Young's a841aa83d patch fixed kexec issue. On UEFI side, this patch changes the logic in the unmapping function. Using the end address of map to calcuate original size. Thank Randy Wright for his report and testing. And also thank Takashi Iwai for his help to trace issue. Cc: Ard Biesheuvel <ard.biesheu...@linaro.org> Cc: Takashi Iwai <ti...@suse.de> Cc: Vivek Goyal <vgo...@redhat.com> Cc: Ingo Molnar <mi...@redhat.com> Tested-by: Randy Wright <rwri...@hpe.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- drivers/firmware/efi/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 5fc7052..1f592d8 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -121,7 +121,7 @@ void __init efi_memmap_unmap(void) if (!efi.memmap.late) { unsigned long size; - size = efi.memmap.desc_size * efi.memmap.nr_map; + size = efi.memmap.map_end - efi.memmap.map; early_memunmap(efi.memmap.map, size); } else { memunmap(efi.memmap.map); -- 2.10.2
[PATCH] efi: Fix the size not consistent issue when unmapping memory map
When using kdump, SOMETIMES the "size not consistent" warning message shows up when the crash kernel boots with early_ioremap_debug parameter: WARNING: CPU: 0 PID: 0 at ../mm/early_ioremap.c:182 early_iounmap+0x4f/0x12c() early_iounmap(ff200180, 0118) [0] size not consistent 0120 The root cause is that the unmapping size of memory map doesn't match with the original size when mapping: in __efi_memmap_init() map.map = early_memremap(phys_map, data->size); in efi_memmap_unmap() size = efi.memmap.desc_size * efi.memmap.nr_map; early_memunmap(efi.memmap.map, size); But the efi.memmap.nr_map is from __efi_memmap_init(). The remainder of size was discarded when calculating the nr_map: map.nr_map = data->size / data->desc_size; When the original size of memory map region does not equal to the result of multiplication. The "size not consistent" warning will be triggered. This issue sometimes was hit by kdump because kexec set the efi map size to align with 16 when loading crash kernel image: in bzImage64_load() efi_map_sz = efi_get_runtime_map_size(); efi_map_sz = ALIGN(efi_map_sz, 16); Dave Young's a841aa83d patch fixed kexec issue. On UEFI side, this patch changes the logic in the unmapping function. Using the end address of map to calcuate original size. Thank Randy Wright for his report and testing. And also thank Takashi Iwai for his help to trace issue. Cc: Ard Biesheuvel Cc: Takashi Iwai Cc: Vivek Goyal Cc: Ingo Molnar Tested-by: Randy Wright Signed-off-by: "Lee, Chun-Yi" --- drivers/firmware/efi/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 5fc7052..1f592d8 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -121,7 +121,7 @@ void __init efi_memmap_unmap(void) if (!efi.memmap.late) { unsigned long size; - size = efi.memmap.desc_size * efi.memmap.nr_map; + size = efi.memmap.map_end - efi.memmap.map; early_memunmap(efi.memmap.map, size); } else { memunmap(efi.memmap.map); -- 2.10.2
[PATCH] efi: Fix the size not consistent issue when unmapping memory map
When using kdump, SOMETIMES the "size not consistent" warning message shows up when the crash kernel boots with early_ioremap_debug parameter: WARNING: CPU: 0 PID: 0 at ../mm/early_ioremap.c:182 early_iounmap+0x4f/0x12c() early_iounmap(ff200180, 0118) [0] size not consistent 0120 The root cause is that the unmapping size of memory map doesn't match with the original size when mapping: in __efi_memmap_init() map.map = early_memremap(phys_map, data->size); in efi_memmap_unmap() size = efi.memmap.desc_size * efi.memmap.nr_map; early_memunmap(efi.memmap.map, size); But the efi.memmap.nr_map is from __efi_memmap_init(). The remainder of size was discarded when calculating the nr_map: map.nr_map = data->size / data->desc_size; When the original size of memory map region does not equal to the result of multiplication. The "size not consistent" warning will be triggered. This issue sometimes was hit by kdump because kexec set the efi map size to align with 16 when loading crash kernel image: in bzImage64_load() efi_map_sz = efi_get_runtime_map_size(); efi_map_sz = ALIGN(efi_map_sz, 16); This patch changes the logic in the unmapping function. Using the end address of map to calcuate original size. Thank Randy Wright for his report and testing. And also thank Takashi Iwai for his help to trace issue. Cc: Ard Biesheuvel <ard.biesheu...@linaro.org> Cc: Randy Wright <rwri...@hpe.com> Cc: Takashi Iwai <ti...@suse.de> Cc: Vivek Goyal <vgo...@redhat.com> Cc: Ingo Molnar <mi...@redhat.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- drivers/firmware/efi/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 5fc7052..1f592d8 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -121,7 +121,7 @@ void __init efi_memmap_unmap(void) if (!efi.memmap.late) { unsigned long size; - size = efi.memmap.desc_size * efi.memmap.nr_map; + size = efi.memmap.map_end - efi.memmap.map; early_memunmap(efi.memmap.map, size); } else { memunmap(efi.memmap.map); -- 2.10.2
[PATCH] efi: Fix the size not consistent issue when unmapping memory map
When using kdump, SOMETIMES the "size not consistent" warning message shows up when the crash kernel boots with early_ioremap_debug parameter: WARNING: CPU: 0 PID: 0 at ../mm/early_ioremap.c:182 early_iounmap+0x4f/0x12c() early_iounmap(ff200180, 0118) [0] size not consistent 0120 The root cause is that the unmapping size of memory map doesn't match with the original size when mapping: in __efi_memmap_init() map.map = early_memremap(phys_map, data->size); in efi_memmap_unmap() size = efi.memmap.desc_size * efi.memmap.nr_map; early_memunmap(efi.memmap.map, size); But the efi.memmap.nr_map is from __efi_memmap_init(). The remainder of size was discarded when calculating the nr_map: map.nr_map = data->size / data->desc_size; When the original size of memory map region does not equal to the result of multiplication. The "size not consistent" warning will be triggered. This issue sometimes was hit by kdump because kexec set the efi map size to align with 16 when loading crash kernel image: in bzImage64_load() efi_map_sz = efi_get_runtime_map_size(); efi_map_sz = ALIGN(efi_map_sz, 16); This patch changes the logic in the unmapping function. Using the end address of map to calcuate original size. Thank Randy Wright for his report and testing. And also thank Takashi Iwai for his help to trace issue. Cc: Ard Biesheuvel Cc: Randy Wright Cc: Takashi Iwai Cc: Vivek Goyal Cc: Ingo Molnar Signed-off-by: "Lee, Chun-Yi" --- drivers/firmware/efi/memmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 5fc7052..1f592d8 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -121,7 +121,7 @@ void __init efi_memmap_unmap(void) if (!efi.memmap.late) { unsigned long size; - size = efi.memmap.desc_size * efi.memmap.nr_map; + size = efi.memmap.map_end - efi.memmap.map; early_memunmap(efi.memmap.map, size); } else { memunmap(efi.memmap.map); -- 2.10.2
[PATCH v2] ACPI / scan: Send the change uevent with offine environmental data
In current design of ACPI container offline, Kernel emits KOBJ_CHANGE uevent to user space to indidate that the ejection of the container was triggered by platform. (caa73ea15 patch) A pure KOBJ_CHANGE uevent is not enough for user space to identify the purpose. For example, a "udevadm trigger" command can also be used to emit change event to all udev rules. A udev rule can not identify that the event is from kernel for offline or from udevadm for other purpose. Then the offline action in udev rule may also be triggered by udevadm tool. So, similar to the change uevent of dock, kernel sends the KOBJ_CHANGE uevent with a offline environmental data to indicate purpose. It's useful by udev rule for using ENV{EVENT} filter. v2: Fix build warning for the environmental string Cc: "Rafael J. Wysocki" <r...@rjwysocki.net> Cc: Len Brown <l...@kernel.org> Acked-by: Michal Hocko <mho...@suse.com> Tested-by: Michal Hocko <mho...@suse.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- drivers/acpi/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e63d93..490498e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; + char *envp[] = { "EVENT=offline", NULL }; /* * acpi_container_offline() calls this for all of the container's @@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) list_for_each_entry(pn, >physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { if (uevent) - kobject_uevent(>dev->kobj, KOBJ_CHANGE); + kobject_uevent_env(>dev->kobj, KOBJ_CHANGE, envp); offline = false; break; -- 2.10.2
[PATCH v2] ACPI / scan: Send the change uevent with offine environmental data
In current design of ACPI container offline, Kernel emits KOBJ_CHANGE uevent to user space to indidate that the ejection of the container was triggered by platform. (caa73ea15 patch) A pure KOBJ_CHANGE uevent is not enough for user space to identify the purpose. For example, a "udevadm trigger" command can also be used to emit change event to all udev rules. A udev rule can not identify that the event is from kernel for offline or from udevadm for other purpose. Then the offline action in udev rule may also be triggered by udevadm tool. So, similar to the change uevent of dock, kernel sends the KOBJ_CHANGE uevent with a offline environmental data to indicate purpose. It's useful by udev rule for using ENV{EVENT} filter. v2: Fix build warning for the environmental string Cc: "Rafael J. Wysocki" Cc: Len Brown Acked-by: Michal Hocko Tested-by: Michal Hocko Signed-off-by: "Lee, Chun-Yi" --- drivers/acpi/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e63d93..490498e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; + char *envp[] = { "EVENT=offline", NULL }; /* * acpi_container_offline() calls this for all of the container's @@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) list_for_each_entry(pn, >physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { if (uevent) - kobject_uevent(>dev->kobj, KOBJ_CHANGE); + kobject_uevent_env(>dev->kobj, KOBJ_CHANGE, envp); offline = false; break; -- 2.10.2
[PATCH 1/5] MODSIGN: do not load mok when secure boot disabled
The mok can not be trusted when the secure boot is disabled. Which means that the kernel embedded certificate is the only trusted key. Due to db/dbx are authenticated variables, they needs manufacturer's KEK for update. So db/dbx are secure when secureboot disabled. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 26 +++--- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index 3d88459..d6de4d0 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,17 +164,6 @@ static int __init load_uefi_certs(void) } } - mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { - rc = parse_efi_signature_list("UEFI:MokListRT", - mok, moksize, get_handler_for_db); - if (rc) - pr_err("Couldn't parse MokListRT signatures: %d\n", rc); - kfree(mok); - } - dbx = get_cert_list(L"dbx", _var, ); if (!dbx) { pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); @@ -187,6 +176,21 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* the MOK can not be trusted when secure boot is disabled */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + + mok = get_cert_list(L"MokListRT", _var, ); + if (!mok) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + kfree(mok); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 1/5] MODSIGN: do not load mok when secure boot disabled
The mok can not be trusted when the secure boot is disabled. Which means that the kernel embedded certificate is the only trusted key. Due to db/dbx are authenticated variables, they needs manufacturer's KEK for update. So db/dbx are secure when secureboot disabled. Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 26 +++--- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index 3d88459..d6de4d0 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,17 +164,6 @@ static int __init load_uefi_certs(void) } } - mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { - rc = parse_efi_signature_list("UEFI:MokListRT", - mok, moksize, get_handler_for_db); - if (rc) - pr_err("Couldn't parse MokListRT signatures: %d\n", rc); - kfree(mok); - } - dbx = get_cert_list(L"dbx", _var, ); if (!dbx) { pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); @@ -187,6 +176,21 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* the MOK can not be trusted when secure boot is disabled */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + + mok = get_cert_list(L"MokListRT", _var, ); + if (!mok) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + kfree(mok); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 0/5 v2] Using the hash in MOKx to blacklist kernel module
This patch set is base on the efi-lock-down and keys-uefi branchs in David Howells's linux-fs git tree. https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-uefi The main purpose is using the MOKx to blacklist kernel module. As the MOK (Machine Owner Key), MOKx is a EFI boot time variable which is maintained by shim boot loader. We can enroll the hash of blacklisted kernel module (with or without signature) to MOKx by mokutil. Kernel loads the hash from MOKx to blacklist keyring when booting. Kernel will prevent to load the kernel module when its hash be found in blacklist. This function is useful to revoke a kernel module that it has exploit. Or revoking a kernel module that it was signed by a unsecure key. Except MOKx, this patch set fixs another two issues: The MOK/MOKx should not be loaded when secure boot is disabled. And, modified error message prints out appropriate status string for reading by human being. v2: Chekcikng the attributes of db and mok before loading certificates. Lee, Chun-Yi (5): MODSIGN: do not load mok when secure boot disabled MODSIGN: print appropriate status message when getting UEFI certificates list MODSIGN: load blacklist from MOKx MODSIGN: checking the blacklisted hash before loading a kernel module MODSIGN: check the attributes of db and mok certs/load_uefi.c | 92 +++-- include/linux/efi.h | 25 ++ kernel/module_signing.c | 62 +++-- 3 files changed, 152 insertions(+), 27 deletions(-) -- 2.10.2
[PATCH 0/5 v2] Using the hash in MOKx to blacklist kernel module
This patch set is base on the efi-lock-down and keys-uefi branchs in David Howells's linux-fs git tree. https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-uefi The main purpose is using the MOKx to blacklist kernel module. As the MOK (Machine Owner Key), MOKx is a EFI boot time variable which is maintained by shim boot loader. We can enroll the hash of blacklisted kernel module (with or without signature) to MOKx by mokutil. Kernel loads the hash from MOKx to blacklist keyring when booting. Kernel will prevent to load the kernel module when its hash be found in blacklist. This function is useful to revoke a kernel module that it has exploit. Or revoking a kernel module that it was signed by a unsecure key. Except MOKx, this patch set fixs another two issues: The MOK/MOKx should not be loaded when secure boot is disabled. And, modified error message prints out appropriate status string for reading by human being. v2: Chekcikng the attributes of db and mok before loading certificates. Lee, Chun-Yi (5): MODSIGN: do not load mok when secure boot disabled MODSIGN: print appropriate status message when getting UEFI certificates list MODSIGN: load blacklist from MOKx MODSIGN: checking the blacklisted hash before loading a kernel module MODSIGN: check the attributes of db and mok certs/load_uefi.c | 92 +++-- include/linux/efi.h | 25 ++ kernel/module_signing.c | 62 +++-- 3 files changed, 152 insertions(+), 27 deletions(-) -- 2.10.2
[PATCH 3/5] MODSIGN: load blacklist from MOKx
This patch adds the logic to load the blacklisted hash and certificates from MOKx which is maintained by shim bootloader. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 16 +--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index f2f372b..dc66a79 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,8 +164,8 @@ static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; - void *db = NULL, *dbx = NULL, *mok = NULL; - unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + void *db = NULL, *dbx = NULL, *mok = NULL, *mokx = NULL; + unsigned long dbsize = 0, dbxsize = 0, moksize = 0, mokxsize = 0; int rc = 0; if (!efi.get_variable) @@ -195,7 +195,7 @@ static int __init load_uefi_certs(void) kfree(dbx); } - /* the MOK can not be trusted when secure boot is disabled */ + /* the MOK and MOKx can not be trusted when secure boot is disabled */ if (!efi_enabled(EFI_SECURE_BOOT)) return 0; @@ -208,6 +208,16 @@ static int __init load_uefi_certs(void) kfree(mok); } + mokx = get_cert_list(L"MokListXRT", _var, ); + if (mokx) { + rc = parse_efi_signature_list("UEFI:mokx", + mokx, mokxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse MokListXRT signatures: %d\n", rc); + kfree(mokx); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 3/5] MODSIGN: load blacklist from MOKx
This patch adds the logic to load the blacklisted hash and certificates from MOKx which is maintained by shim bootloader. Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 16 +--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index f2f372b..dc66a79 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,8 +164,8 @@ static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; - void *db = NULL, *dbx = NULL, *mok = NULL; - unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + void *db = NULL, *dbx = NULL, *mok = NULL, *mokx = NULL; + unsigned long dbsize = 0, dbxsize = 0, moksize = 0, mokxsize = 0; int rc = 0; if (!efi.get_variable) @@ -195,7 +195,7 @@ static int __init load_uefi_certs(void) kfree(dbx); } - /* the MOK can not be trusted when secure boot is disabled */ + /* the MOK and MOKx can not be trusted when secure boot is disabled */ if (!efi_enabled(EFI_SECURE_BOOT)) return 0; @@ -208,6 +208,16 @@ static int __init load_uefi_certs(void) kfree(mok); } + mokx = get_cert_list(L"MokListXRT", _var, ); + if (mokx) { + rc = parse_efi_signature_list("UEFI:mokx", + mokx, mokxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse MokListXRT signatures: %d\n", rc); + kfree(mokx); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 2/5] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_T
[PATCH 2/5] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_TOO_SMALL) + EFI_STATUS_STR(NOT_READY) + EFI_STATUS_STR(DEVICE_ERROR) + EFI_STATUS_STR(WRITE_PROTECTED) + EFI_STATUS_ST
[PATCH 4/5] MODSIGN: checking the blacklisted hash before loading a kernel module
This patch adds the logic for checking the kernel module's hash base on blacklist. The hash must be generated by sha256 and enrolled to dbx/mokx. For example: sha256sum sample.ko mokutil --mokx --import-hash $HASH_RESULT Whether the signature on ko file is stripped or not, the hash can be compared by kernel. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- kernel/module_signing.c | 62 +++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -11,9 +11,12 @@ #include #include +#include #include #include #include +#include +#include #include "module-internal.h" enum pkey_id_type { @@ -42,19 +45,67 @@ struct module_signature { __be32 sig_len;/* Length of signature data */ }; +static int mod_is_hash_blacklisted(const void *mod, size_t verifylen) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + u8 *digest; + int ret = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + goto error_return; + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest memory buffer allocate fail\n"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *)digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + + ret = crypto_shash_finup(desc, mod, verifylen, digest); + if (ret < 0) + goto error_shash; + + pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest); + + ret = is_hash_blacklisted(digest, digest_size, "bin"); + if (ret == -EKEYREJECTED) + pr_err("Module hash %*phN is blacklisted\n", + (int) digest_size, digest); + +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); +error_return: + return ret; +} + /* * Verify the signature on a module. */ int mod_verify_sig(const void *mod, unsigned long *_modlen) { struct module_signature ms; - size_t modlen = *_modlen, sig_len; + size_t modlen = *_modlen, sig_len, wholelen; + int ret; pr_devel("==>%s(,%zu)\n", __func__, modlen); if (modlen <= sizeof(ms)) return -EBADMSG; + wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1; memcpy(, mod + (modlen - sizeof(ms)), sizeof(ms)); modlen -= sizeof(ms); @@ -80,7 +131,14 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, (void *)1UL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); + pr_devel("verify_pkcs7_signature() = %d\n", ret); + + /* checking hash of module is in blacklist */ + if (!ret) + ret = mod_is_hash_blacklisted(mod, wholelen); + + return ret; } -- 2.10.2
[PATCH 5/5] MODSIGN: check the attributes of db and mok
That's better for checking the attributes of db and mok variables before loading certificates to kernel keyring. For db and dbx, both of them are authenticated variables. Which means that they can only be modified by manufacturer's key. So the kernel should checks EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute before we trust it. For mok-rt and mokx-rt, both of them are created by shim boot loader to forward the mok/mokx content to runtime. They must be runtime-volatile variables. So kernel should checks that the attributes map did not set EFI_VARIABLE_NON_VOLATILE bit before we trust it. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 35 +++ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index dc66a79..52526bd 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -33,7 +33,8 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } -static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status, + u32 attr) { char *utf8_str; unsigned long utf8_size; @@ -46,8 +47,8 @@ static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) return; ucs2_as_utf8(utf8_str, char16_str, utf8_size); - pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", - utf8_str, efi_status_to_str(status)); + pr_info("MODSIGN: Couldn't get UEFI %s: %sAttributes: 0x%016x\n", + utf8_str, efi_status_to_str(status), attr); kfree(utf8_str); } @@ -55,12 +56,13 @@ static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) * Get a certificate list blob from the named EFI variable. */ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, - unsigned long *size) + unsigned long *size, u32 pos_attr, u32 neg_attr) { efi_status_t status; unsigned long lsize = 4; unsigned long tmpdb[4]; void *db; + u32 attr = 0; status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { @@ -75,17 +77,22 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, goto err; } - status = efi.get_variable(name, guid, NULL, , db); + status = efi.get_variable(name, guid, , , db); if (status != EFI_SUCCESS) { - kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - goto err; + goto free; } + /* must have positive attributes and no negative attributes */ + if ((pos_attr && !(attr & pos_attr)) || + (neg_attr && (attr & neg_attr))) + goto free; *size = lsize; return db; +free: + kfree(db); err: - print_get_fail(name, status); + print_get_fail(name, status, attr); return NULL; } @@ -175,7 +182,8 @@ static int __init load_uefi_certs(void) * an error if we can't get them. */ if (!uefi_check_ignore_db()) { - db = get_cert_list(L"db", _var, ); + db = get_cert_list(L"db", _var, , + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); @@ -185,7 +193,8 @@ static int __init load_uefi_certs(void) } } - dbx = get_cert_list(L"dbx", _var, ); + dbx = get_cert_list(L"dbx", _var, , + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, @@ -199,7 +208,8 @@ static int __init load_uefi_certs(void) if (!efi_enabled(EFI_SECURE_BOOT)) return 0; - mok = get_cert_list(L"MokListRT", _var, ); + mok = get_cert_list(L"MokListRT", _var, , + 0, EFI_VARIABLE_NON_VOLATILE); if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); @@ -208,7 +218,8 @@ static int __init load_uefi_certs(void) kfree(mok); } - mokx = get_cert_list
[PATCH 5/5] MODSIGN: check the attributes of db and mok
That's better for checking the attributes of db and mok variables before loading certificates to kernel keyring. For db and dbx, both of them are authenticated variables. Which means that they can only be modified by manufacturer's key. So the kernel should checks EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute before we trust it. For mok-rt and mokx-rt, both of them are created by shim boot loader to forward the mok/mokx content to runtime. They must be runtime-volatile variables. So kernel should checks that the attributes map did not set EFI_VARIABLE_NON_VOLATILE bit before we trust it. Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 35 +++ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index dc66a79..52526bd 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -33,7 +33,8 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } -static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status, + u32 attr) { char *utf8_str; unsigned long utf8_size; @@ -46,8 +47,8 @@ static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) return; ucs2_as_utf8(utf8_str, char16_str, utf8_size); - pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", - utf8_str, efi_status_to_str(status)); + pr_info("MODSIGN: Couldn't get UEFI %s: %sAttributes: 0x%016x\n", + utf8_str, efi_status_to_str(status), attr); kfree(utf8_str); } @@ -55,12 +56,13 @@ static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) * Get a certificate list blob from the named EFI variable. */ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, - unsigned long *size) + unsigned long *size, u32 pos_attr, u32 neg_attr) { efi_status_t status; unsigned long lsize = 4; unsigned long tmpdb[4]; void *db; + u32 attr = 0; status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { @@ -75,17 +77,22 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, goto err; } - status = efi.get_variable(name, guid, NULL, , db); + status = efi.get_variable(name, guid, , , db); if (status != EFI_SUCCESS) { - kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - goto err; + goto free; } + /* must have positive attributes and no negative attributes */ + if ((pos_attr && !(attr & pos_attr)) || + (neg_attr && (attr & neg_attr))) + goto free; *size = lsize; return db; +free: + kfree(db); err: - print_get_fail(name, status); + print_get_fail(name, status, attr); return NULL; } @@ -175,7 +182,8 @@ static int __init load_uefi_certs(void) * an error if we can't get them. */ if (!uefi_check_ignore_db()) { - db = get_cert_list(L"db", _var, ); + db = get_cert_list(L"db", _var, , + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); @@ -185,7 +193,8 @@ static int __init load_uefi_certs(void) } } - dbx = get_cert_list(L"dbx", _var, ); + dbx = get_cert_list(L"dbx", _var, , + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, @@ -199,7 +208,8 @@ static int __init load_uefi_certs(void) if (!efi_enabled(EFI_SECURE_BOOT)) return 0; - mok = get_cert_list(L"MokListRT", _var, ); + mok = get_cert_list(L"MokListRT", _var, , + 0, EFI_VARIABLE_NON_VOLATILE); if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); @@ -208,7 +218,8 @@ static int __init load_uefi_certs(void) kfree(mok); } - mokx = get_cert_list(L"MokListXRT", _var, ); + mokx = get_cert_list(L"MokListXRT", _var, , + 0, EFI_VARIABLE_NON_VOLATILE); if (mokx) { rc = parse_efi_signature_list("UEFI:mokx", mokx, mokxsize, -- 2.10.2
[PATCH 4/5] MODSIGN: checking the blacklisted hash before loading a kernel module
This patch adds the logic for checking the kernel module's hash base on blacklist. The hash must be generated by sha256 and enrolled to dbx/mokx. For example: sha256sum sample.ko mokutil --mokx --import-hash $HASH_RESULT Whether the signature on ko file is stripped or not, the hash can be compared by kernel. Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" --- kernel/module_signing.c | 62 +++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -11,9 +11,12 @@ #include #include +#include #include #include #include +#include +#include #include "module-internal.h" enum pkey_id_type { @@ -42,19 +45,67 @@ struct module_signature { __be32 sig_len;/* Length of signature data */ }; +static int mod_is_hash_blacklisted(const void *mod, size_t verifylen) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + u8 *digest; + int ret = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + goto error_return; + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest memory buffer allocate fail\n"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *)digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + + ret = crypto_shash_finup(desc, mod, verifylen, digest); + if (ret < 0) + goto error_shash; + + pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest); + + ret = is_hash_blacklisted(digest, digest_size, "bin"); + if (ret == -EKEYREJECTED) + pr_err("Module hash %*phN is blacklisted\n", + (int) digest_size, digest); + +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); +error_return: + return ret; +} + /* * Verify the signature on a module. */ int mod_verify_sig(const void *mod, unsigned long *_modlen) { struct module_signature ms; - size_t modlen = *_modlen, sig_len; + size_t modlen = *_modlen, sig_len, wholelen; + int ret; pr_devel("==>%s(,%zu)\n", __func__, modlen); if (modlen <= sizeof(ms)) return -EBADMSG; + wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1; memcpy(, mod + (modlen - sizeof(ms)), sizeof(ms)); modlen -= sizeof(ms); @@ -80,7 +131,14 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, (void *)1UL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); + pr_devel("verify_pkcs7_signature() = %d\n", ret); + + /* checking hash of module is in blacklist */ + if (!ret) + ret = mod_is_hash_blacklisted(mod, wholelen); + + return ret; } -- 2.10.2
[PATCH 1/5] MODSIGN: do not load mok when secure boot disabled
The mok can not be trusted when the secure boot is disabled. Which means that the kernel embedded certificate is the only trusted key. Due to db/dbx are authenticated variables, they needs manufacturer's KEK for update. So db/dbx are secure when secureboot disabled. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: Lee, Chun-Yi <j...@suse.com> --- certs/load_uefi.c | 26 +++--- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index 3d88459..d6de4d0 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,17 +164,6 @@ static int __init load_uefi_certs(void) } } - mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { - rc = parse_efi_signature_list("UEFI:MokListRT", - mok, moksize, get_handler_for_db); - if (rc) - pr_err("Couldn't parse MokListRT signatures: %d\n", rc); - kfree(mok); - } - dbx = get_cert_list(L"dbx", _var, ); if (!dbx) { pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); @@ -187,6 +176,21 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* the MOK can not be trusted when secure boot is disabled */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + + mok = get_cert_list(L"MokListRT", _var, ); + if (!mok) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + kfree(mok); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 1/5] MODSIGN: do not load mok when secure boot disabled
The mok can not be trusted when the secure boot is disabled. Which means that the kernel embedded certificate is the only trusted key. Due to db/dbx are authenticated variables, they needs manufacturer's KEK for update. So db/dbx are secure when secureboot disabled. Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: Lee, Chun-Yi --- certs/load_uefi.c | 26 +++--- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index 3d88459..d6de4d0 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,17 +164,6 @@ static int __init load_uefi_certs(void) } } - mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { - rc = parse_efi_signature_list("UEFI:MokListRT", - mok, moksize, get_handler_for_db); - if (rc) - pr_err("Couldn't parse MokListRT signatures: %d\n", rc); - kfree(mok); - } - dbx = get_cert_list(L"dbx", _var, ); if (!dbx) { pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); @@ -187,6 +176,21 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* the MOK can not be trusted when secure boot is disabled */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + + mok = get_cert_list(L"MokListRT", _var, ); + if (!mok) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + kfree(mok); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 2/5] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Cc: James Bottomley <james.bottom...@hansenpartnership.com> Signed-off-by: Lee, Chun-Yi <j...@suse.com> --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_TOO_SMALL)
[PATCH 2/5] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells Cc: Josh Boyer Cc: James Bottomley Signed-off-by: Lee, Chun-Yi --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_TOO_SMALL) + EFI_STATUS_STR(NOT_READY) + EFI_STATUS_STR(DEVICE_ERROR) + EFI_STATUS_STR(WRITE_PROTECTED) + EFI_STATUS_STR(OUT_OF_R
[PATCH 0/5 v2] Using the hash in MOKx to blacklist kernel module
This patch set is base on the efi-lock-down and keys-uefi branchs in David Howells's linux-fs git tree. https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-uefi The main purpose is using the MOKx to blacklist kernel module. As the MOK (Machine Owner Key), MOKx is a EFI boot time variable which is maintained by shim boot loader. We can enroll the hash of blacklisted kernel module (with or without signature) to MOKx by mokutil. Kernel loads the hash from MOKx to blacklist keyring when booting. Kernel will prevent to load the kernel module when its hash be found in blacklist. This function is useful to revoke a kernel module that it has exploit. Or revoking a kernel module that it was signed by a unsecure key. Except MOKx, this patch set fixs another two issues: The MOK/MOKx should not be loaded when secure boot is disabled. And, modified error message prints out appropriate status string for reading by human being. v2: Chekcikng the attributes of db and mok before loading certificates. Lee, Chun-Yi (5): MODSIGN: do not load mok when secure boot disabled MODSIGN: print appropriate status message when getting UEFI certificates list MODSIGN: load blacklist from MOKx MODSIGN: checking the blacklisted hash before loading a kernel module MODSIGN: check the attributes of db and mok certs/load_uefi.c | 92 +++-- include/linux/efi.h | 25 ++ kernel/module_signing.c | 62 +++-- 3 files changed, 152 insertions(+), 27 deletions(-) -- 2.10.2
[PATCH 0/5 v2] Using the hash in MOKx to blacklist kernel module
This patch set is base on the efi-lock-down and keys-uefi branchs in David Howells's linux-fs git tree. https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-uefi The main purpose is using the MOKx to blacklist kernel module. As the MOK (Machine Owner Key), MOKx is a EFI boot time variable which is maintained by shim boot loader. We can enroll the hash of blacklisted kernel module (with or without signature) to MOKx by mokutil. Kernel loads the hash from MOKx to blacklist keyring when booting. Kernel will prevent to load the kernel module when its hash be found in blacklist. This function is useful to revoke a kernel module that it has exploit. Or revoking a kernel module that it was signed by a unsecure key. Except MOKx, this patch set fixs another two issues: The MOK/MOKx should not be loaded when secure boot is disabled. And, modified error message prints out appropriate status string for reading by human being. v2: Chekcikng the attributes of db and mok before loading certificates. Lee, Chun-Yi (5): MODSIGN: do not load mok when secure boot disabled MODSIGN: print appropriate status message when getting UEFI certificates list MODSIGN: load blacklist from MOKx MODSIGN: checking the blacklisted hash before loading a kernel module MODSIGN: check the attributes of db and mok certs/load_uefi.c | 92 +++-- include/linux/efi.h | 25 ++ kernel/module_signing.c | 62 +++-- 3 files changed, 152 insertions(+), 27 deletions(-) -- 2.10.2
[PATCH] ACPI / scan: Send the change uevent with offine environmental data
In current design of ACPI container offline, Kernel emits KOBJ_CHANGE uevent to user space to indidate that the ejection of the container was triggered by platform. (caa73ea15 patch) A pure KOBJ_CHANGE uevent is not enough for user space to identify the purpose. For example, a "udevadm trigger" command can also be used to emit change event to all udev rules. A udev rule can not identify that the event is from kernel for offline or from udevadm for other purpose. Then the offline action in udev rule may also be triggered by udevadm tool. So, similar to the change uevent of dock, kernel sends the KOBJ_CHANGE uevent with a offline environmental data to indicate purpose. It's useful by udev rule for using ENV{EVENT} filter. Cc: Michal Hocko <mho...@kernel.org> Cc: "Rafael J. Wysocki" <r...@rjwysocki.net> Cc: Len Brown <l...@kernel.org> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- drivers/acpi/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e63d93..f6dca9b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; + static const char *envp[] = { "EVENT=offline", NULL }; /* * acpi_container_offline() calls this for all of the container's @@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) list_for_each_entry(pn, >physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { if (uevent) - kobject_uevent(>dev->kobj, KOBJ_CHANGE); + kobject_uevent_env(>dev->kobj, KOBJ_CHANGE, envp); offline = false; break; -- 2.10.2
[PATCH] ACPI / scan: Send the change uevent with offine environmental data
In current design of ACPI container offline, Kernel emits KOBJ_CHANGE uevent to user space to indidate that the ejection of the container was triggered by platform. (caa73ea15 patch) A pure KOBJ_CHANGE uevent is not enough for user space to identify the purpose. For example, a "udevadm trigger" command can also be used to emit change event to all udev rules. A udev rule can not identify that the event is from kernel for offline or from udevadm for other purpose. Then the offline action in udev rule may also be triggered by udevadm tool. So, similar to the change uevent of dock, kernel sends the KOBJ_CHANGE uevent with a offline environmental data to indicate purpose. It's useful by udev rule for using ENV{EVENT} filter. Cc: Michal Hocko Cc: "Rafael J. Wysocki" Cc: Len Brown Signed-off-by: "Lee, Chun-Yi" --- drivers/acpi/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e63d93..f6dca9b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -116,6 +116,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; + static const char *envp[] = { "EVENT=offline", NULL }; /* * acpi_container_offline() calls this for all of the container's @@ -126,7 +127,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) list_for_each_entry(pn, >physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { if (uevent) - kobject_uevent(>dev->kobj, KOBJ_CHANGE); + kobject_uevent_env(>dev->kobj, KOBJ_CHANGE, envp); offline = false; break; -- 2.10.2
[PATCH 2/4] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_TOO_SMALL) + EFI_STATUS_STR(NOT_READY) + EFI_STATUS_STR(DEVICE_ERROR)
[PATCH 2/4] MODSIGN: print appropriate status message when getting UEFI certificates list
When getting certificates list from UEFI variable, the original error message shows the state number from UEFI firmware. It's hard to be read by human. This patch changed the error message to show the appropriate string. The message will be showed as: [0.788529] MODSIGN: Couldn't get UEFI MokListRT: EFI_NOT_FOUND [0.788537] MODSIGN: Couldn't get UEFI MokListXRT: EFI_NOT_FOUND Cc: David Howells Cc: Josh Boyer Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 43 ++- include/linux/efi.h | 25 + 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index d6de4d0..f2f372b 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -32,6 +33,24 @@ static __init bool uefi_check_ignore_db(void) return status == EFI_SUCCESS; } +static __init void print_get_fail(efi_char16_t *char16_str, efi_status_t status) +{ + char *utf8_str; + unsigned long utf8_size; + + if (!char16_str) + return; + utf8_size = ucs2_utf8size(char16_str) + 1; + utf8_str = kmalloc(utf8_size, GFP_KERNEL); + if (!utf8_str) + return; + ucs2_as_utf8(utf8_str, char16_str, utf8_size); + + pr_info("MODSIGN: Couldn't get UEFI %s: %s\n", + utf8_str, efi_status_to_str(status)); + kfree(utf8_str); +} + /* * Get a certificate list blob from the named EFI variable. */ @@ -45,25 +64,29 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, status = efi.get_variable(name, guid, NULL, , ); if (status != EFI_BUFFER_TOO_SMALL) { - pr_err("Couldn't get size: 0x%lx\n", status); - return NULL; + if (status != EFI_NOT_FOUND) + pr_err("Couldn't get size: 0x%lx\n", status); + goto err; } db = kmalloc(lsize, GFP_KERNEL); if (!db) { pr_err("Couldn't allocate memory for uefi cert list\n"); - return NULL; + goto err; } status = efi.get_variable(name, guid, NULL, , db); if (status != EFI_SUCCESS) { kfree(db); pr_err("Error reading db var: 0x%lx\n", status); - return NULL; + goto err; } *size = lsize; return db; +err: + print_get_fail(name, status); + return NULL; } /* @@ -153,9 +176,7 @@ static int __init load_uefi_certs(void) */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", _var, ); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { + if (db) { rc = parse_efi_signature_list("UEFI:db", db, dbsize, get_handler_for_db); if (rc) @@ -165,9 +186,7 @@ static int __init load_uefi_certs(void) } dbx = get_cert_list(L"dbx", _var, ); - if (!dbx) { - pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); - } else { + if (dbx) { rc = parse_efi_signature_list("UEFI:dbx", dbx, dbxsize, get_handler_for_dbx); @@ -181,9 +200,7 @@ static int __init load_uefi_certs(void) return 0; mok = get_cert_list(L"MokListRT", _var, ); - if (!mok) { - pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); - } else { + if (mok) { rc = parse_efi_signature_list("UEFI:MokListRT", mok, moksize, get_handler_for_db); if (rc) diff --git a/include/linux/efi.h b/include/linux/efi.h index 2729d6f..c44946c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1600,4 +1600,29 @@ struct linux_efi_random_seed { u8 bits[]; }; +#define EFI_STATUS_STR(_status)\ + case EFI_##_status: \ + return "EFI_" __stringify(_status); \ + +static inline char * +efi_status_to_str(efi_status_t status) +{ + switch (status) { + EFI_STATUS_STR(SUCCESS) + EFI_STATUS_STR(LOAD_ERROR) + EFI_STATUS_STR(INVALID_PARAMETER) + EFI_STATUS_STR(UNSUPPORTED) + EFI_STATUS_STR(BAD_BUFFER_SIZE) + EFI_STATUS_STR(BUFFER_TOO_SMALL) + EFI_STATUS_STR(NOT_READY) + EFI_STATUS_STR(DEVICE_ERROR) + EFI_STATUS_STR(WRITE_PROTECTED) + EFI_STATUS_STR(OUT_OF_RESOURC
[PATCH 3/4] MODSIGN: load blacklist from MOKx
This patch adds the logic to load the blacklisted hash and certificates from MOKx which is maintained by shim bootloader. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- certs/load_uefi.c | 16 +--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index f2f372b..dc66a79 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,8 +164,8 @@ static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; - void *db = NULL, *dbx = NULL, *mok = NULL; - unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + void *db = NULL, *dbx = NULL, *mok = NULL, *mokx = NULL; + unsigned long dbsize = 0, dbxsize = 0, moksize = 0, mokxsize = 0; int rc = 0; if (!efi.get_variable) @@ -195,7 +195,7 @@ static int __init load_uefi_certs(void) kfree(dbx); } - /* the MOK can not be trusted when secure boot is disabled */ + /* the MOK and MOKx can not be trusted when secure boot is disabled */ if (!efi_enabled(EFI_SECURE_BOOT)) return 0; @@ -208,6 +208,16 @@ static int __init load_uefi_certs(void) kfree(mok); } + mokx = get_cert_list(L"MokListXRT", _var, ); + if (mokx) { + rc = parse_efi_signature_list("UEFI:mokx", + mokx, mokxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse MokListXRT signatures: %d\n", rc); + kfree(mokx); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 3/4] MODSIGN: load blacklist from MOKx
This patch adds the logic to load the blacklisted hash and certificates from MOKx which is maintained by shim bootloader. Cc: David Howells Cc: Josh Boyer Signed-off-by: "Lee, Chun-Yi" --- certs/load_uefi.c | 16 +--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/certs/load_uefi.c b/certs/load_uefi.c index f2f372b..dc66a79 100644 --- a/certs/load_uefi.c +++ b/certs/load_uefi.c @@ -164,8 +164,8 @@ static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; - void *db = NULL, *dbx = NULL, *mok = NULL; - unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + void *db = NULL, *dbx = NULL, *mok = NULL, *mokx = NULL; + unsigned long dbsize = 0, dbxsize = 0, moksize = 0, mokxsize = 0; int rc = 0; if (!efi.get_variable) @@ -195,7 +195,7 @@ static int __init load_uefi_certs(void) kfree(dbx); } - /* the MOK can not be trusted when secure boot is disabled */ + /* the MOK and MOKx can not be trusted when secure boot is disabled */ if (!efi_enabled(EFI_SECURE_BOOT)) return 0; @@ -208,6 +208,16 @@ static int __init load_uefi_certs(void) kfree(mok); } + mokx = get_cert_list(L"MokListXRT", _var, ); + if (mokx) { + rc = parse_efi_signature_list("UEFI:mokx", + mokx, mokxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse MokListXRT signatures: %d\n", rc); + kfree(mokx); + } + return rc; } late_initcall(load_uefi_certs); -- 2.10.2
[PATCH 4/4] MODSIGN: checking the blacklisted hash before loading a kernel module
This patch adds the logic for checking the kernel module's hash base on blacklist. The hash must be generated by sha256 and enrolled to dbx/mokx. For example: sha256sum sample.ko mokutil --mokx --import-hash $HASH_RESULT Whether the signature on ko file is stripped or not, the hash can be compared by kernel. Cc: David Howells <dhowe...@redhat.com> Cc: Josh Boyer <jwbo...@fedoraproject.org> Signed-off-by: "Lee, Chun-Yi" <j...@suse.com> --- kernel/module_signing.c | 62 +++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -11,9 +11,12 @@ #include #include +#include #include #include #include +#include +#include #include "module-internal.h" enum pkey_id_type { @@ -42,19 +45,67 @@ struct module_signature { __be32 sig_len;/* Length of signature data */ }; +static int mod_is_hash_blacklisted(const void *mod, size_t verifylen) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + u8 *digest; + int ret = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + goto error_return; + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest memory buffer allocate fail\n"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *)digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + + ret = crypto_shash_finup(desc, mod, verifylen, digest); + if (ret < 0) + goto error_shash; + + pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest); + + ret = is_hash_blacklisted(digest, digest_size, "bin"); + if (ret == -EKEYREJECTED) + pr_err("Module hash %*phN is blacklisted\n", + (int) digest_size, digest); + +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); +error_return: + return ret; +} + /* * Verify the signature on a module. */ int mod_verify_sig(const void *mod, unsigned long *_modlen) { struct module_signature ms; - size_t modlen = *_modlen, sig_len; + size_t modlen = *_modlen, sig_len, wholelen; + int ret; pr_devel("==>%s(,%zu)\n", __func__, modlen); if (modlen <= sizeof(ms)) return -EBADMSG; + wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1; memcpy(, mod + (modlen - sizeof(ms)), sizeof(ms)); modlen -= sizeof(ms); @@ -80,7 +131,14 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, (void *)1UL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); + pr_devel("verify_pkcs7_signature() = %d\n", ret); + + /* checking hash of module is in blacklist */ + if (!ret) + ret = mod_is_hash_blacklisted(mod, wholelen); + + return ret; } -- 2.10.2
[PATCH 4/4] MODSIGN: checking the blacklisted hash before loading a kernel module
This patch adds the logic for checking the kernel module's hash base on blacklist. The hash must be generated by sha256 and enrolled to dbx/mokx. For example: sha256sum sample.ko mokutil --mokx --import-hash $HASH_RESULT Whether the signature on ko file is stripped or not, the hash can be compared by kernel. Cc: David Howells Cc: Josh Boyer Signed-off-by: "Lee, Chun-Yi" --- kernel/module_signing.c | 62 +++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -11,9 +11,12 @@ #include #include +#include #include #include #include +#include +#include #include "module-internal.h" enum pkey_id_type { @@ -42,19 +45,67 @@ struct module_signature { __be32 sig_len;/* Length of signature data */ }; +static int mod_is_hash_blacklisted(const void *mod, size_t verifylen) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + u8 *digest; + int ret = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + goto error_return; + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); + if (!digest) { + pr_err("digest memory buffer allocate fail\n"); + ret = -ENOMEM; + goto error_digest; + } + desc = (void *)digest + digest_size; + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(desc); + if (ret < 0) + goto error_shash; + + ret = crypto_shash_finup(desc, mod, verifylen, digest); + if (ret < 0) + goto error_shash; + + pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest); + + ret = is_hash_blacklisted(digest, digest_size, "bin"); + if (ret == -EKEYREJECTED) + pr_err("Module hash %*phN is blacklisted\n", + (int) digest_size, digest); + +error_shash: + kfree(digest); +error_digest: + crypto_free_shash(tfm); +error_return: + return ret; +} + /* * Verify the signature on a module. */ int mod_verify_sig(const void *mod, unsigned long *_modlen) { struct module_signature ms; - size_t modlen = *_modlen, sig_len; + size_t modlen = *_modlen, sig_len, wholelen; + int ret; pr_devel("==>%s(,%zu)\n", __func__, modlen); if (modlen <= sizeof(ms)) return -EBADMSG; + wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1; memcpy(, mod + (modlen - sizeof(ms)), sizeof(ms)); modlen -= sizeof(ms); @@ -80,7 +131,14 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) return -EBADMSG; } - return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, (void *)1UL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); + pr_devel("verify_pkcs7_signature() = %d\n", ret); + + /* checking hash of module is in blacklist */ + if (!ret) + ret = mod_is_hash_blacklisted(mod, wholelen); + + return ret; } -- 2.10.2