[PATCH v5,4/4] Documentation/admin-guide/module-signing.rst: add openssl command option example for CodeSign EKU

2021-04-11 Thread Lee, Chun-Yi
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

2021-04-11 Thread Lee, Chun-Yi
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

2021-04-11 Thread Lee, Chun-Yi
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

2021-04-11 Thread Lee, Chun-Yi
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

2021-04-11 Thread Lee, Chun-Yi
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

2021-04-08 Thread Lee, Chun-Yi
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

2021-04-08 Thread Lee, Chun-Yi
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

2021-04-08 Thread Lee, Chun-Yi
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

2021-04-08 Thread Lee, Chun-Yi
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

2021-04-08 Thread Lee, Chun-Yi
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

2021-03-22 Thread Lee, Chun-Yi
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

2021-03-22 Thread Lee, Chun-Yi
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

2021-03-22 Thread Lee, Chun-Yi
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

2021-03-22 Thread Lee, Chun-Yi
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

2021-03-22 Thread Lee, Chun-Yi
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

2021-03-09 Thread Lee, Chun-Yi
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

2021-03-09 Thread Lee, Chun-Yi
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

2021-03-09 Thread Lee, Chun-Yi
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

2021-03-09 Thread Lee, Chun-Yi
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

2021-03-09 Thread Lee, Chun-Yi
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

2021-02-21 Thread Lee, Chun-Yi
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

2021-02-21 Thread Lee, Chun-Yi
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

2021-02-21 Thread Lee, Chun-Yi
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

2021-02-21 Thread Lee, Chun-Yi
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

2021-02-21 Thread Lee, Chun-Yi
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

2021-01-20 Thread Lee, Chun-Yi
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

2021-01-20 Thread Lee, Chun-Yi
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

2021-01-20 Thread Lee, Chun-Yi
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

2021-01-20 Thread Lee, Chun-Yi
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

2021-01-20 Thread Lee, Chun-Yi
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

2020-11-24 Thread Lee, Chun-Yi
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

2020-11-24 Thread Lee, Chun-Yi
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

2020-11-24 Thread Lee, Chun-Yi
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

2020-11-24 Thread Lee, Chun-Yi
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

2020-11-24 Thread Lee, Chun-Yi
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

2020-11-04 Thread Lee, Chun-Yi
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

2020-11-04 Thread Lee, Chun-Yi
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

2020-11-04 Thread Lee, Chun-Yi
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

2020-10-20 Thread Lee, Chun-Yi
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

2020-10-20 Thread Lee, Chun-Yi
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

2020-10-20 Thread Lee, Chun-Yi
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

2020-09-24 Thread Lee, Chun-Yi
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

2020-08-25 Thread Lee, Chun-Yi
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

2020-08-25 Thread Lee, Chun-Yi
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

2020-08-19 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2019-01-03 Thread Lee, Chun-Yi
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

2018-12-30 Thread Lee, Chun-Yi
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

2018-12-30 Thread Lee, Chun-Yi
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

2018-12-30 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-09-12 Thread Lee, Chun-Yi
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

2018-07-24 Thread Lee, Chun-Yi
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

2018-07-24 Thread Lee, Chun-Yi
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

2018-05-02 Thread Lee, Chun-Yi
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

2018-05-02 Thread Lee, Chun-Yi
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

2018-04-13 Thread Lee, Chun-Yi
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

2018-04-13 Thread Lee, Chun-Yi
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

2018-03-19 Thread Lee, Chun-Yi
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

2018-03-19 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-13 Thread Lee, Chun-Yi
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

2018-03-01 Thread Lee, Chun-Yi
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

2018-03-01 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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

2017-11-29 Thread Lee, Chun-Yi
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



  1   2   3   4   5   >