hulk inclusion category: feature feature: digest-lists
---------------------------
A fake IMA xattr is created to perform EVM verification even if security.ima is not present. Appraisal could succeed if EVM status is unknown and the file digest is found in a digest list.
This patch allocates a larger buffer to store fake IMA xattrs (struct evm_ima_xattr_data can be used only for SHA1 digests).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_appraise.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index a11577147022..c6376ec28ccd 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -193,19 +193,19 @@ int ima_appraise_measurement(enum ima_hooks func, struct dentry *dentry = file_dentry(file); struct inode *inode = d_backing_inode(dentry); enum integrity_status status = INTEGRITY_UNKNOWN; - struct evm_ima_xattr_data digest_list_value; + char _buf[sizeof(struct evm_ima_xattr_data) + SHA512_DIGEST_SIZE]; int rc = xattr_len, hash_start = 0;
if (!(inode->i_opflags & IOP_XATTR)) return INTEGRITY_UNKNOWN;
if (rc == -ENODATA && found_digest && - !(file->f_mode && FMODE_CREATED)) { - digest_list_value.type = EVM_IMA_XATTR_DIGEST_LIST; - digest_list_value.digest[0] = found_digest->algo; - memcpy(&digest_list_value.digest[1], found_digest->digest, + !(file->f_mode & FMODE_CREATED)) { + xattr_value = (struct evm_ima_xattr_data *)_buf; + xattr_value->type = IMA_XATTR_DIGEST_NG; + xattr_value->digest[0] = found_digest->algo; + memcpy(&xattr_value->digest[1], found_digest->digest, hash_digest_size[found_digest->algo]); - xattr_value = &digest_list_value; rc = hash_digest_size[found_digest->algo] + 2; }
@@ -283,7 +283,6 @@ int ima_appraise_measurement(enum ima_hooks func, status = INTEGRITY_PASS; break; } - if (!ima_appraise_no_metadata) { cause = "IMA-xattr-untrusted"; status = INTEGRITY_FAIL;
hulk inclusion category: feature feature: digest-lists
---------------------------
The meta_immutable requirement is necessary only for executables to ensure that the correct label is applied to the new process.
For other hooks, the imasig requirement is sufficient as it ensures data provenance and wouldn't allow access of data that can be modified locally in the system.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_policy.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 94ba751a8191..fa5ce0de932b 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -551,9 +551,6 @@ void __init ima_init_policy(void) * signatures, prior to any other appraise rules. */ for (i = 0; i < secure_boot_entries; i++) { - if (ima_use_appraise_exec_immutable) - secure_boot_rules[i].flags |= - IMA_META_IMMUTABLE_REQUIRED; list_add_tail(&secure_boot_rules[i].list, &ima_default_rules); temp_ima_appraise |= ima_appraise_flag(secure_boot_rules[i].func); @@ -594,7 +591,8 @@ void __init ima_init_policy(void) }
for (i = 0; i < appraise_exec_entries; i++) { - if (ima_use_appraise_exec_immutable) + if (ima_use_appraise_exec_immutable && + appraise_exec_rules[i].func == BPRM_CHECK) appraise_exec_rules[i].flags |= IMA_META_IMMUTABLE_REQUIRED; list_add_tail(&appraise_exec_rules[i].list,
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch moves the meta_immutable check after the check for the EVM status. Even if IMA would continue appraisal verification when the status is not good (e.g. INTEGRITY_UNKNOWN), this patch refuses any status different from INTEGRITY_PASS_IMMUTABLE if the meta_immutable requirement was specified.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_appraise.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index c6376ec28ccd..2f77f086df36 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -229,14 +229,6 @@ int ima_appraise_measurement(enum ima_hooks func, switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE: - if (iint->flags & IMA_META_IMMUTABLE_REQUIRED && - status != INTEGRITY_PASS_IMMUTABLE) { - status = INTEGRITY_FAIL; - cause = "metadata-modifiable"; - integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, - filename, op, cause, rc, 0); - goto out; - } break; case INTEGRITY_UNKNOWN: if (ima_appraise_req_evm && @@ -264,6 +256,15 @@ int ima_appraise_measurement(enum ima_hooks func, WARN_ONCE(true, "Unexpected integrity status %d\n", status); }
+ if ((iint->flags & IMA_META_IMMUTABLE_REQUIRED) && + status != INTEGRITY_PASS_IMMUTABLE) { + status = INTEGRITY_FAIL; + cause = "metadata-modifiable"; + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, + filename, op, cause, rc, 0); + goto out; + } + switch (xattr_value->type) { case EVM_IMA_XATTR_DIGEST_LIST: set_bit(IMA_DIGEST_LIST, &iint->atomic_flags);
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch changes the type of the IMA xattr to IMA_XATTR_DIGEST_NG. This would allow to pass EVM verification even if security.ima is not present. Metadata digest would be calculated as if the xattr in the disk was passed to EVM.
This patch also removes duplicate processing for digest-based xattr.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_appraise.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2f77f086df36..bd00611b3cec 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -265,30 +265,18 @@ int ima_appraise_measurement(enum ima_hooks func, goto out; }
- switch (xattr_value->type) { - case EVM_IMA_XATTR_DIGEST_LIST: + if (found_digest && status != INTEGRITY_PASS && + status != INTEGRITY_PASS_IMMUTABLE) set_bit(IMA_DIGEST_LIST, &iint->atomic_flags);
- if (found_digest) { - if (!ima_digest_is_immutable(found_digest)) { - if (iint->flags & IMA_DIGSIG_REQUIRED) { - cause = "IMA-signature-required"; - status = INTEGRITY_FAIL; - break; - } - clear_bit(IMA_DIGSIG, &iint->atomic_flags); - } else { - set_bit(IMA_DIGSIG, &iint->atomic_flags); - } - - status = INTEGRITY_PASS; - break; - } + switch (xattr_value->type) { + case EVM_IMA_XATTR_DIGEST_LIST: if (!ima_appraise_no_metadata) { cause = "IMA-xattr-untrusted"; status = INTEGRITY_FAIL; break; } + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); /* fall through */ case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ @@ -300,7 +288,8 @@ int ima_appraise_measurement(enum ima_hooks func, status = INTEGRITY_FAIL; break; } - if (status != INTEGRITY_PASS_IMMUTABLE) { + if (status != INTEGRITY_PASS_IMMUTABLE && + (!found_digest || !ima_digest_is_immutable(found_digest))) { if (iint->flags & IMA_DIGSIG_REQUIRED) { cause = "IMA-signature-required"; status = INTEGRITY_FAIL;
hulk inclusion category: feature feature: digest-lists
---------------------------
Without resetting the status when security.evm is modified, IMA appraisal would continue to use the cached result and wouldn't detect whether the meta_immutable requirement is still satisfied.
This patch calls evm_reset_status() in the post hooks when security.evm is modified.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/evm/evm_main.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index ece39b80ca9c..5762336b5513 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -180,7 +180,6 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, evm_status = INTEGRITY_FAIL; goto out; } - digest.hdr.algo = HASH_ALGO_SHA1; rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &digest); @@ -562,12 +561,17 @@ static void evm_reset_status(struct inode *inode, int bit) void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { + int is_evm = !strcmp(xattr_name, XATTR_NAME_EVM); + if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name) - && !posix_xattr_acl(xattr_name))) + && !posix_xattr_acl(xattr_name) && !is_evm)) return;
evm_reset_status(dentry->d_inode, IMA_CHANGE_XATTR);
+ if (is_evm) + return; + evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); }
@@ -583,11 +587,16 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, */ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { - if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) + int is_evm = !strcmp(xattr_name, XATTR_NAME_EVM); + + if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name) && !is_evm)) return;
evm_reset_status(dentry->d_inode, IMA_CHANGE_XATTR);
+ if (is_evm) + return; + evm_update_evmxattr(dentry, xattr_name, NULL, 0); }
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch adds the file name to the log messages, so that it is possible to find why digest list lookup was disabled.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_digest_list.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 7e901bdb4340..c7610d9d603c 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -217,7 +217,8 @@ void ima_check_measured_appraised(struct file *file)
iint = integrity_iint_find(file_inode(file)); if (!iint) { - pr_err("disabling digest lists lookup\n"); + pr_err("%s not processed, disabling digest lists lookup\n", + file_dentry(file)->d_name.name); ima_digest_list_actions = 0; return; } @@ -225,14 +226,16 @@ void ima_check_measured_appraised(struct file *file) mutex_lock(&iint->mutex); if ((ima_digest_list_actions & IMA_MEASURE) && !(iint->flags & IMA_MEASURED)) { - pr_err("disabling digest lists lookup for measurement\n"); + pr_err("%s not measured, disabling digest lists lookup " + "for measurement\n", file_dentry(file)->d_name.name); ima_digest_list_actions &= ~IMA_MEASURE; }
if ((ima_digest_list_actions & IMA_APPRAISE) && (!(iint->flags & IMA_APPRAISED) || !test_bit(IMA_DIGSIG, &iint->atomic_flags))) { - pr_err("disabling digest lists lookup for appraisal\n"); + pr_err("%s not appraised, disabling digest lists lookup " + "for appraisal\n", file_dentry(file)->d_name.name); ima_digest_list_actions &= ~IMA_APPRAISE; }
hulk inclusion category: feature feature: digest-lists
---------------------------
In some cases digest lists already exists (e.g. RPM headers) and only their content is signed.
Allowing appraisal of digest lists without metadata won't be an issue as they must be immutable and even if corrupted metadata allow unauthorized read, system integrity won't be compromised, as the integrity of digest lists is assumed to be good.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_appraise.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index bd00611b3cec..78b7f2fcd7d8 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -244,6 +244,16 @@ int ima_appraise_measurement(enum ima_hooks func, */ if (ima_appraise_no_metadata && found_digest) break; + /* Allow access to digest lists without metadata, only if they + * are signed or found in a digest list (immutable) + */ + if (func == DIGEST_LIST_CHECK) { + if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) + break; + if (found_digest && + ima_digest_is_immutable(found_digest)) + break; + } cause = "missing-HMAC"; goto out; case INTEGRITY_FAIL_IMMUTABLE:
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch adds also a fake EVM xattr to search metadata digest in the digest lists, so that verification can pass even if both security.evm and security.ima are missing.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/evm/evm_main.c | 35 ++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 5762336b5513..aae1cb7603ac 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -94,7 +94,7 @@ static bool evm_key_loaded(void) return (bool)(evm_initialized & EVM_KEY_MASK); }
-static int evm_find_protected_xattrs(struct dentry *dentry) +static int evm_find_protected_xattrs(struct dentry *dentry, int *ima_present) { struct inode *inode = d_backing_inode(dentry); struct xattr_list *xattr; @@ -111,6 +111,8 @@ static int evm_find_protected_xattrs(struct dentry *dentry) continue; return error; } + if (!strcmp(xattr->name, XATTR_NAME_IMA)) + *ima_present = 1; count++; }
@@ -138,11 +140,14 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, { struct evm_ima_xattr_data *xattr_data = NULL; struct signature_v2_hdr *hdr; - enum integrity_status evm_status = INTEGRITY_PASS; + enum integrity_status evm_status = INTEGRITY_PASS, saved_evm_status; struct evm_digest digest; struct ima_digest *found_digest; struct inode *inode; - int rc, xattr_len, evm_immutable = 0; + struct signature_v2_hdr evm_fake_xattr = { + .type = EVM_IMA_XATTR_DIGEST_LIST, + .version = 2, .hash_algo = HASH_ALGO_SHA256 }; + int rc, xattr_len, evm_immutable = 0, ima_present = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS || iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) @@ -156,7 +161,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, if (rc <= 0) { evm_status = INTEGRITY_FAIL; if (rc == -ENODATA) { - rc = evm_find_protected_xattrs(dentry); + rc = evm_find_protected_xattrs(dentry, &ima_present); if (rc > 0) evm_status = INTEGRITY_NOLABEL; else if (rc == 0) @@ -164,7 +169,20 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } - goto out; + /* IMA added a fake xattr, set also EVM fake xattr */ + if (!ima_present && xattr_name && + !strcmp(xattr_name, XATTR_NAME_IMA) && + xattr_value_len >= sizeof(struct evm_ima_xattr_data)) { + evm_fake_xattr.hash_algo = + ((struct evm_ima_xattr_data *)xattr_value)->digest[0]; + xattr_data = + (struct evm_ima_xattr_data *)&evm_fake_xattr; + rc = sizeof(evm_fake_xattr); + } + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) + goto out; + + saved_evm_status = evm_status; }
xattr_len = rc; @@ -274,14 +292,17 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, break; }
- if (rc) + if (rc && xattr_data == (struct evm_ima_xattr_data *)&evm_fake_xattr) + evm_status = saved_evm_status; + else if (rc) evm_status = (rc == -ENODATA) ? INTEGRITY_NOXATTRS : evm_immutable ? INTEGRITY_FAIL_IMMUTABLE : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; - kfree(xattr_data); + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) + kfree(xattr_data); return evm_status; }
hulk inclusion category: feature feature: digest-lists
---------------------------
Introduce the new function to get the number of bits and bytes from an MPI.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/mpi.h | 2 ++ lib/mpi/mpicoder.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/include/linux/mpi.h b/include/linux/mpi.h index 7cd1473c64a4..56187bb57c78 100644 --- a/include/linux/mpi.h +++ b/include/linux/mpi.h @@ -61,6 +61,8 @@ int mpi_resize(MPI a, unsigned nlimbs);
/*-- mpicoder.c --*/ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes); +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg); MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread); MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int len); void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign); diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index eead4b339466..207cb3b91a51 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -78,22 +78,41 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) } EXPORT_SYMBOL_GPL(mpi_read_raw_data);
-MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) +int mpi_key_length(const void *xbuffer, unsigned int ret_nread, + unsigned int *nbits_arg, unsigned int *nbytes_arg) { const uint8_t *buffer = xbuffer; - unsigned int nbits, nbytes; - MPI val; + unsigned int nbits;
- if (*ret_nread < 2) - return ERR_PTR(-EINVAL); + if (ret_nread < 2) + return -EINVAL; nbits = buffer[0] << 8 | buffer[1];
if (nbits > MAX_EXTERN_MPI_BITS) { pr_info("MPI: mpi too large (%u bits)\n", nbits); - return ERR_PTR(-EINVAL); + return -EINVAL; }
- nbytes = DIV_ROUND_UP(nbits, 8); + if (nbits_arg) + *nbits_arg = nbits; + if (nbytes_arg) + *nbytes_arg = DIV_ROUND_UP(nbits, 8); + + return 0; +} +EXPORT_SYMBOL_GPL(mpi_key_length); + +MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) +{ + const uint8_t *buffer = xbuffer; + unsigned int nbytes; + MPI val; + int ret; + + ret = mpi_key_length(xbuffer, *ret_nread, NULL, &nbytes); + if (ret < 0) + return ERR_PTR(ret); + if (nbytes + 2 > *ret_nread) { pr_info("MPI: mpi larger than buffer nbytes=%u ret_nread=%u\n", nbytes, *ret_nread);
hulk inclusion category: feature feature: digest-lists
---------------------------
Parse the RSA key with RAW format if the ASN.1 parser returns an error.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- crypto/rsa.c | 14 +++++-- crypto/rsa_helper.c | 69 +++++++++++++++++++++++++++++++++++ include/crypto/internal/rsa.h | 6 +++ 3 files changed, 85 insertions(+), 4 deletions(-)
diff --git a/crypto/rsa.c b/crypto/rsa.c index 4167980c243d..d33aa039de6e 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -275,8 +275,11 @@ static int rsa_set_pub_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key);
ret = rsa_parse_pub_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_pub_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + }
mpi_key->e = mpi_read_raw_data(raw_key.e, raw_key.e_sz); if (!mpi_key->e) @@ -309,8 +312,11 @@ static int rsa_set_priv_key(struct crypto_akcipher *tfm, const void *key, rsa_free_mpi_key(mpi_key);
ret = rsa_parse_priv_key(&raw_key, key, keylen); - if (ret) - return ret; + if (ret) { + ret = rsa_parse_priv_key_raw(&raw_key, key, keylen); + if (ret) + return ret; + }
mpi_key->d = mpi_read_raw_data(raw_key.d, raw_key.d_sz); if (!mpi_key->d) diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c index efc78fe7ae2e..fda85fbe2e38 100644 --- a/crypto/rsa_helper.c +++ b/crypto/rsa_helper.c @@ -14,6 +14,7 @@ #include <linux/export.h> #include <linux/err.h> #include <linux/fips.h> +#include <linux/mpi.h> #include <crypto/internal/rsa.h> #include "rsapubkey.asn1.h" #include "rsaprivkey.asn1.h" @@ -153,6 +154,32 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag, return 0; }
+typedef int (*rsa_get_func)(void *, size_t, unsigned char, + const void *, size_t); + +static int rsa_parse_key_raw(struct rsa_key *rsa_key, + const void *key, unsigned int key_len, + rsa_get_func *func, int n_func) +{ + unsigned int nbytes, len = key_len; + const void *key_ptr = key; + int ret, i; + + for (i = 0; i < n_func; i++) { + ret = mpi_key_length(key_ptr, len, NULL, &nbytes); + if (ret < 0) + return ret; + + ret = func[i](rsa_key, 0, 0, key_ptr + 2, nbytes); + if (ret < 0) + return ret; + + key_ptr += nbytes + 2; + } + + return (key_ptr == key + key_len) ? 0 : -EINVAL; +} + /** * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key as is, @@ -171,6 +198,27 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, } EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+/** + * rsa_parse_pub_key_raw() - parse the RAW key and store in the provided struct + * rsa_key, pointers to the raw key as is, so that + * the caller can copy it or MPI parse it, etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func pub_func[] = {rsa_get_n, rsa_get_e}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + pub_func, ARRAY_SIZE(pub_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_pub_key_raw); + /** * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the * provided struct rsa_key, pointers to the raw key @@ -189,3 +237,24 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len); } EXPORT_SYMBOL_GPL(rsa_parse_priv_key); + +/** + * rsa_parse_priv_key_raw() - parse the RAW key and store in the provided struct + * rsa_key, pointers to the raw key as is, so that + * the caller can copy it or MPI parse it, etc. + * + * @rsa_key: struct rsa_key key representation + * @key: key in RAW format + * @key_len: length of key + * + * Return: 0 on success or error code in case of error + */ +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len) +{ + rsa_get_func priv_func[] = {rsa_get_n, rsa_get_e, rsa_get_d}; + + return rsa_parse_key_raw(rsa_key, key, key_len, + priv_func, ARRAY_SIZE(priv_func)); +} +EXPORT_SYMBOL_GPL(rsa_parse_priv_key_raw); diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index 9e8f1590de98..6bc672daa175 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -55,8 +55,14 @@ struct rsa_key { int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key, unsigned int key_len);
+int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key, + unsigned int key_len); + extern struct crypto_template rsa_pkcs1pad_tmpl; #endif
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: digest-lists
---------------------------
Provide some useful PGP definitions from RFC 4880. These describe details of public key crypto as used by crypto keys for things like signature verification.
Changelog:
v0: - fix style issues (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- include/linux/pgp.h | 216 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 include/linux/pgp.h
diff --git a/include/linux/pgp.h b/include/linux/pgp.h new file mode 100644 index 000000000000..71186ac3128b --- /dev/null +++ b/include/linux/pgp.h @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP definitions (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGP_H +#define _LINUX_PGP_H + +#include <linux/types.h> + +struct pgp_key_ID { + u8 id[8]; +} __packed; + +struct pgp_time { + u8 time[4]; +} __packed; + +/* + * PGP public-key algorithm identifiers [RFC4880: 9.1] + */ +enum pgp_pubkey_algo { + PGP_PUBKEY_RSA_ENC_OR_SIG = 1, + PGP_PUBKEY_RSA_ENC_ONLY = 2, + PGP_PUBKEY_RSA_SIG_ONLY = 3, + PGP_PUBKEY_ELGAMAL = 16, + PGP_PUBKEY_DSA = 17, + PGP_PUBKEY__LAST +}; + +/* + * PGP symmetric-key algorithm identifiers [RFC4880: 9.2] + */ +enum pgp_symkey_algo { + PGP_SYMKEY_PLAINTEXT = 0, + PGP_SYMKEY_IDEA = 1, + PGP_SYMKEY_3DES = 2, + PGP_SYMKEY_CAST5 = 3, + PGP_SYMKEY_BLOWFISH = 4, + PGP_SYMKEY_AES_128KEY = 7, + PGP_SYMKEY_AES_192KEY = 8, + PGP_SYMKEY_AES_256KEY = 9, + PGP_SYMKEY_TWOFISH_256KEY = 10, +}; + +/* + * PGP compression algorithm identifiers [RFC4880: 9.3] + */ +enum pgp_compr_algo { + PGP_COMPR_UNCOMPRESSED = 0, + PGP_COMPR_ZIP = 1, + PGP_COMPR_ZLIB = 2, + PGP_COMPR_BZIP2 = 3, +}; + +/* + * PGP hash algorithm identifiers [RFC4880: 9.4] + */ +enum pgp_hash_algo { + PGP_HASH_MD5 = 1, + PGP_HASH_SHA1 = 2, + PGP_HASH_RIPE_MD_160 = 3, + PGP_HASH_SHA256 = 8, + PGP_HASH_SHA384 = 9, + PGP_HASH_SHA512 = 10, + PGP_HASH_SHA224 = 11, + PGP_HASH__LAST +}; + +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST]; + +/* + * PGP packet type tags [RFC4880: 4.3]. + */ +enum pgp_packet_tag { + PGP_PKT_RESERVED = 0, + PGP_PKT_PUBKEY_ENC_SESSION_KEY = 1, + PGP_PKT_SIGNATURE = 2, + PGP_PKT_SYMKEY_ENC_SESSION_KEY = 3, + PGP_PKT_ONEPASS_SIGNATURE = 4, + PGP_PKT_SECRET_KEY = 5, + PGP_PKT_PUBLIC_KEY = 6, + PGP_PKT_SECRET_SUBKEY = 7, + PGP_PKT_COMPRESSED_DATA = 8, + PGP_PKT_SYM_ENC_DATA = 9, + PGP_PKT_MARKER = 10, + PGP_PKT_LITERAL_DATA = 11, + PGP_PKT_TRUST = 12, + PGP_PKT_USER_ID = 13, + PGP_PKT_PUBLIC_SUBKEY = 14, + PGP_PKT_USER_ATTRIBUTE = 17, + PGP_PKT_SYM_ENC_AND_INTEG_DATA = 18, + PGP_PKT_MODIFY_DETECT_CODE = 19, + PGP_PKT_PRIVATE_0 = 60, + PGP_PKT_PRIVATE_3 = 63, + PGP_PKT__HIGHEST = 63 +}; + +/* + * Signature (tag 2) packet [RFC4880: 5.2]. + */ +enum pgp_signature_version { + PGP_SIG_VERSION_3 = 3, + PGP_SIG_VERSION_4 = 4, +}; + +enum pgp_signature_type { + PGP_SIG_BINARY_DOCUMENT_SIG = 0x00, + PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01, + PGP_SIG_STANDALONE_SIG = 0x02, + PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY = 0x10, + PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11, + PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY = 0x12, + PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY = 0x13, + PGP_SIG_SUBKEY_BINDING_SIG = 0x18, + PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19, + PGP_SIG_DIRECTLY_ON_KEY = 0x1F, + PGP_SIG_KEY_REVOCATION_SIG = 0x20, + PGP_SIG_SUBKEY_REVOCATION_SIG = 0x28, + PGP_SIG_CERT_REVOCATION_SIG = 0x30, + PGP_SIG_TIMESTAMP_SIG = 0x40, + PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50, +}; + +struct pgp_signature_v3_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */ + u8 length_of_hashed; /* == 5 */ + struct { + enum pgp_signature_type signature_type : 8; + struct pgp_time creation_time; + } __packed hashed; + struct pgp_key_ID issuer; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +struct pgp_signature_v4_packet { + enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_4 */ + enum pgp_signature_type signature_type : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + enum pgp_hash_algo hash_algo : 8; +} __packed; + +/* + * V4 signature subpacket types [RFC4880: 5.2.3.1]. + */ +enum pgp_sig_subpkt_type { + PGP_SIG_CREATION_TIME = 2, + PGP_SIG_EXPIRATION_TIME = 3, + PGP_SIG_EXPORTABLE_CERT = 4, + PGP_SIG_TRUST_SIG = 5, + PGP_SIG_REGEXP = 6, + PGP_SIG_REVOCABLE = 7, + PGP_SIG_KEY_EXPIRATION_TIME = 9, + PGP_SIG_PREF_SYM_ALGO = 11, + PGP_SIG_REVOCATION_KEY = 12, + PGP_SIG_ISSUER = 16, + PGP_SIG_NOTATION_DATA = 20, + PGP_SIG_PREF_HASH_ALGO = 21, + PGP_SIG_PREF_COMPR_ALGO = 22, + PGP_SIG_KEY_SERVER_PREFS = 23, + PGP_SIG_PREF_KEY_SERVER = 24, + PGP_SIG_PRIMARY_USER_ID = 25, + PGP_SIG_POLICY_URI = 26, + PGP_SIG_KEY_FLAGS = 27, + PGP_SIG_SIGNERS_USER_ID = 28, + PGP_SIG_REASON_FOR_REVOCATION = 29, + PGP_SIG_FEATURES = 30, + PGP_SIG_TARGET = 31, + PGP_SIG_EMBEDDED_SIG = 32, + PGP_SIG__LAST +}; + +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK 0x80 + +/* + * Key (tag 5, 6, 7 and 14) packet + */ +enum pgp_key_version { + PGP_KEY_VERSION_2 = 2, + PGP_KEY_VERSION_3 = 3, + PGP_KEY_VERSION_4 = 4, +}; + +struct pgp_key_v3_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + u8 expiry[2]; /* 0 or time in days till expiry */ + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +struct pgp_key_v4_packet { + enum pgp_key_version version : 8; + struct pgp_time creation_time; + enum pgp_pubkey_algo pubkey_algo : 8; + u8 key_material[0]; +} __packed; + +/* + * Literal Data (tag 11) packet + */ +enum pgp_literal_data_format { + PGP_LIT_FORMAT_BINARY = 0x62, + PGP_LIT_FORMAT_TEXT = 0x74, + PGP_LIT_FORMAT_TEXT_UTF8 = 0x75, +}; + +#endif /* _LINUX_PGP_H */
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: digest-lists
---------------------------
Provide a simple parser that extracts the packets from a PGP packet blob and passes the desirous ones to the given processor function:
struct pgp_parse_context { u64 types_of_interest; int (*process_packet)(struct pgp_parse_context *context, enum pgp_packet_tag type, u8 headerlen, const u8 *data, size_t datalen); };
int pgp_parse_packets(const u8 *data, size_t datalen, struct pgp_parse_context *ctx);
This is configured on with CONFIG_PGP_LIBRARY.
Changelog
v0: - fix style issues (Roberto Sassu) - declare pgp_to_public_key_algo (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- crypto/asymmetric_keys/Kconfig | 6 + crypto/asymmetric_keys/Makefile | 5 + crypto/asymmetric_keys/pgp_library.c | 281 +++++++++++++++++++++++++++ crypto/asymmetric_keys/pgp_parser.h | 23 +++ include/linux/pgplib.h | 48 +++++ 5 files changed, 363 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_library.c create mode 100644 crypto/asymmetric_keys/pgp_parser.h create mode 100644 include/linux/pgplib.h
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index d8a73d94bb30..37b3bb7d59f0 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -65,4 +65,10 @@ config SIGNED_PE_FILE_VERIFICATION This option provides support for verifying the signature(s) on a signed PE binary.
+config PGP_LIBRARY + tristate "PGP parsing library" + help + This option enables a library that provides a number of simple + utility functions for parsing PGP (RFC 4880) packet-based messages. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index d4b2e1b2dc65..966b67409ec3 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -61,3 +61,8 @@ verify_signed_pefile-y := \
$(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h + +# +# PGP handling +# +obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c new file mode 100644 index 000000000000..e2a1dda707f0 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_library.c @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP packet parser (RFC 4880) + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGPL: "fmt +#include <linux/pgplib.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> + +MODULE_LICENSE("GPL"); + +const char *const pgp_hash_algorithms[PGP_HASH__LAST] = { + [PGP_HASH_MD5] = "md5", + [PGP_HASH_SHA1] = "sha1", + [PGP_HASH_RIPE_MD_160] = "rmd160", + [PGP_HASH_SHA256] = "sha256", + [PGP_HASH_SHA384] = "sha384", + [PGP_HASH_SHA512] = "sha512", + [PGP_HASH_SHA224] = "sha224", +}; +EXPORT_SYMBOL_GPL(pgp_hash_algorithms); + +const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST] = { + [PGP_PUBKEY_RSA_ENC_OR_SIG] = "rsa", + [PGP_PUBKEY_RSA_ENC_ONLY] = "rsa", + [PGP_PUBKEY_RSA_SIG_ONLY] = "rsa", + [PGP_PUBKEY_ELGAMAL] = NULL, + [PGP_PUBKEY_DSA] = NULL, +}; +EXPORT_SYMBOL_GPL(pgp_to_public_key_algo); + +/** + * pgp_parse_packet_header - Parse a PGP packet header + * @_data: Start of the PGP packet (updated to PGP packet data) + * @_datalen: Amount of data remaining in buffer (decreased) + * @_type: Where the packet type will be returned + * @_headerlen: Where the header length will be returned + * + * Parse a set of PGP packet header [RFC 4880: 4.2]. + * + * Returns packet data size on success; non-zero on error. If successful, + * *_data and *_datalen will have been updated and *_headerlen will be set to + * hold the length of the packet header. + */ +static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen, + enum pgp_packet_tag *_type, + u8 *_headerlen) +{ + enum pgp_packet_tag type; + const u8 *data = *_data; + size_t size, datalen = *_datalen; + + pr_devel("-->%s(,%zu,,)\n", __func__, datalen); + + if (datalen < 2) + goto short_packet; + + pr_devel("pkthdr %02x, %02x\n", data[0], data[1]); + + type = *data++; + datalen--; + if (!(type & 0x80)) { + pr_debug("Packet type does not have MSB set\n"); + return -EBADMSG; + } + type &= ~0x80; + + if (type & 0x40) { + /* New packet length format */ + type &= ~0x40; + pr_devel("new format: t=%u\n", type); + switch (data[0]) { + case 0x00 ... 0xbf: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 0xc0 ... 0xdf: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = (data[0] - 192) * 256; + size += data[1] + 192; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 0xff: + /* Five-byte length */ + if (datalen < 5) + goto short_packet; + size = data[1] << 24; + size |= data[2] << 16; + size |= data[3] << 8; + size |= data[4]; + data += 5; + datalen -= 5; + *_headerlen = 6; + break; + default: + pr_debug("Partial body length packet not supported\n"); + return -EBADMSG; + } + } else { + /* Old packet length format */ + u8 length_type = type & 0x03; + + type >>= 2; + pr_devel("old format: t=%u lt=%u\n", type, length_type); + + switch (length_type) { + case 0: + /* One-byte length */ + size = data[0]; + data++; + datalen--; + *_headerlen = 2; + break; + case 1: + /* Two-byte length */ + if (datalen < 2) + goto short_packet; + size = data[0] << 8; + size |= data[1]; + data += 2; + datalen -= 2; + *_headerlen = 3; + break; + case 2: + /* Four-byte length */ + if (datalen < 4) + goto short_packet; + size = data[0] << 24; + size |= data[1] << 16; + size |= data[2] << 8; + size |= data[3]; + data += 4; + datalen -= 4; + *_headerlen = 5; + break; + default: + pr_debug("Indefinite length packet not supported\n"); + return -EBADMSG; + } + } + + pr_devel("datalen=%zu size=%zu", datalen, size); + if (datalen < size) + goto short_packet; + if ((int)size < 0) + goto too_big; + + *_data = data; + *_datalen = datalen; + *_type = type; + pr_devel("Found packet type=%u size=%zd\n", type, size); + return size; + +short_packet: + pr_debug("Attempt to parse short packet\n"); + return -EBADMSG; +too_big: + pr_debug("Signature subpacket size >2G\n"); + return -EMSGSIZE; +} + +/** + * pgp_parse_packets - Parse a set of PGP packets + * @_data: Data to be parsed (updated) + * @_datalen: Amount of data (updated) + * @ctx: Parsing context + * + * Parse a set of PGP packets [RFC 4880: 4]. + */ +int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx) +{ + enum pgp_packet_tag type; + ssize_t pktlen; + u8 headerlen; + int ret; + + while (datalen > 2) { + pktlen = pgp_parse_packet_header(&data, &datalen, &type, + &headerlen); + if (pktlen < 0) + return pktlen; + + if ((ctx->types_of_interest >> type) & 1) { + ret = ctx->process_packet(ctx, type, headerlen, + data, pktlen); + if (ret < 0) + return ret; + } + data += pktlen; + datalen -= pktlen; + } + + if (datalen != 0) { + pr_debug("Excess octets in packet stream\n"); + return -EBADMSG; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_packets); + +/** + * pgp_parse_public_key - Parse the common part of a PGP pubkey packet + * @_data: Content of packet (updated) + * @_datalen: Length of packet remaining (updated) + * @pk: Public key data + * + * Parse the common data struct for a PGP pubkey packet [RFC 4880: 5.5.2]. + */ +int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk) +{ + const u8 *data = *_data; + size_t datalen = *_datalen; + unsigned int tmp; + + if (datalen < 12) { + pr_debug("Public key packet too short\n"); + return -EBADMSG; + } + + pk->version = *data++; + switch (pk->version) { + case PGP_KEY_VERSION_2: + case PGP_KEY_VERSION_3: + case PGP_KEY_VERSION_4: + break; + default: + pr_debug("Public key packet with unhandled version %d\n", + pk->version); + return -EBADMSG; + } + + tmp = *data++ << 24; + tmp |= *data++ << 16; + tmp |= *data++ << 8; + tmp |= *data++; + pk->creation_time = tmp; + if (pk->version == PGP_KEY_VERSION_4) { + pk->expires_at = 0; /* Have to get it from the selfsignature */ + } else { + unsigned short ndays; + + ndays = *data++ << 8; + ndays |= *data++; + if (ndays) + pk->expires_at = pk->creation_time + ndays * 86400UL; + else + pk->expires_at = 0; + datalen -= 2; + } + + pk->pubkey_algo = *data++; + datalen -= 6; + + pr_devel("%x,%x,%lx,%lx\n", + pk->version, pk->pubkey_algo, pk->creation_time, + pk->expires_at); + + *_data = data; + *_datalen = datalen; + return 0; +} +EXPORT_SYMBOL_GPL(pgp_parse_public_key); diff --git a/crypto/asymmetric_keys/pgp_parser.h b/crypto/asymmetric_keys/pgp_parser.h new file mode 100644 index 000000000000..56f9a2b48679 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_parser.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP crypto data parser internal definitions + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/pgp.h> + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * pgp_library.c + */ +extern const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST]; diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h new file mode 100644 index 000000000000..51fcda15236c --- /dev/null +++ b/include/linux/pgplib.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* PGP library definitions (RFC 4880) + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_PGPLIB_H +#define _LINUX_PGPLIB_H + +#if IS_ENABLED(CONFIG_PGP_LIBRARY) || IS_ENABLED(CONFIG_PGP_LIBRARY_MODULE) + +#include <linux/pgp.h> + +/* + * PGP library packet parser + */ +struct pgp_parse_context { + u64 types_of_interest; + int (*process_packet)(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen); +}; + +extern int pgp_parse_packets(const u8 *data, size_t datalen, + struct pgp_parse_context *ctx); + +struct pgp_parse_pubkey { + enum pgp_key_version version : 8; + enum pgp_pubkey_algo pubkey_algo : 8; + time_t creation_time; + time_t expires_at; +}; + +extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen, + struct pgp_parse_pubkey *pk); + + +#endif /* CONFIG_PGP_LIBRARY */ + +#endif /* _LINUX_PGPLIB_H */
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: digest-lists
---------------------------
Implement a PGP data parser for the crypto key type to use when instantiating a key.
This parser attempts to parse the instantiation data as a PGP packet sequence (RFC 4880) and if it parses okay, attempts to extract a public-key algorithm key or subkey from it.
If it finds such a key, it will set up a public_key subtype payload with appropriate handler routines (DSA or RSA) and attach it to the key.
Thanks to Tetsuo Handa penguin-kernel@I-love.SAKURA.ne.jp for pointing out some errors.
Changelog
v0: - remove declaration of pgp_to_public_key_algo (Roberto Sassu) - replace prep->type_data with prep->payload.data (Roberto Sassu) - replace public_key_destroy() with public_key_free() (Roberto Sassu) - use asymmetric_key_generate_id() to generate the key identifiers (Roberto Sassu) - add raw_fingerprint to pgp_key_data_parse_context structure (Roberto Sassu) - replace algorithm identifiers with strings (Roberto Sassu) - don't parse MPIs (Roberto Sassu) - don't process Public-Subkey packets (only one key will be created) (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- crypto/asymmetric_keys/Kconfig | 11 + crypto/asymmetric_keys/Makefile | 4 + crypto/asymmetric_keys/pgp_public_key.c | 339 ++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_public_key.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 37b3bb7d59f0..8ba0e6ac084f 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -71,4 +71,15 @@ config PGP_LIBRARY This option enables a library that provides a number of simple utility functions for parsing PGP (RFC 4880) packet-based messages.
+config PGP_KEY_PARSER + tristate "PGP key parser" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PGP_LIBRARY + select MD5 # V3 fingerprint generation + select SHA1 # V4 fingerprint generation + help + This option provides support for parsing PGP (RFC 4880) format blobs + for key data and provides the ability to instantiate a crypto key + from a public key packet found inside the blob. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 966b67409ec3..6a5256f64723 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -66,3 +66,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h # PGP handling # obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o + +obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o +pgp_key_parser-y := \ + pgp_public_key.o diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c new file mode 100644 index 000000000000..2845b5d1a765 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Instantiate a public key crypto key from PGP format data [RFC 4880] + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PGP: "fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mpi.h> +#include <linux/pgplib.h> +#include <keys/asymmetric-subtype.h> +#include <keys/asymmetric-parser.h> +#include <crypto/hash.h> +#include <crypto/public_key.h> +#include "pgp_parser.h" + +#define MAX_MPI 5 + +MODULE_LICENSE("GPL"); + +static inline void digest_putc(struct shash_desc *digest, uint8_t ch) +{ + crypto_shash_update(digest, &ch, 1); +} + +struct pgp_key_data_parse_context { + struct pgp_parse_context pgp; + struct public_key *pub; + unsigned char *raw_fingerprint; + char *fingerprint; +}; + +/* + * Calculate the public key ID (RFC4880 12.2) + */ +static int pgp_calc_pkey_keyid(struct shash_desc *digest, + struct pgp_parse_pubkey *pgp, + struct public_key *pub) +{ + unsigned int nb[MAX_MPI]; + unsigned int nn[MAX_MPI]; + unsigned int n; + size_t keylen = pub->keylen; + u8 *key_ptr = pub->key; + u8 *pp[MAX_MPI]; + u32 a32; + int npkey; + int i, ret; + + kenter(""); + + n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6; + for (i = 0; i < MAX_MPI && keylen > 0; i++) { + ret = mpi_key_length(key_ptr, keylen, nb + i, nn + i); + if (ret < 0) + return ret; + + pp[i] = key_ptr + 2; + key_ptr += 2 + nn[i]; + keylen -= 2 + nn[i]; + n += 2 + nn[i]; + } + + if (keylen != 0) { + pr_debug("excess %zu\n", keylen); + return -EBADMSG; + } + + npkey = i; + + digest_putc(digest, 0x99); /* ctb */ + digest_putc(digest, n >> 8); /* 16-bit header length */ + digest_putc(digest, n); + digest_putc(digest, pgp->version); + + a32 = pgp->creation_time; + digest_putc(digest, a32 >> 24); + digest_putc(digest, a32 >> 16); + digest_putc(digest, a32 >> 8); + digest_putc(digest, a32 >> 0); + + if (pgp->version < PGP_KEY_VERSION_4) { + u16 a16; + + if (pgp->expires_at) + a16 = (pgp->expires_at - pgp->creation_time) / 86400UL; + else + a16 = 0; + digest_putc(digest, a16 >> 8); + digest_putc(digest, a16 >> 0); + } + + digest_putc(digest, pgp->pubkey_algo); + + for (i = 0; i < npkey; i++) { + digest_putc(digest, nb[i] >> 8); + digest_putc(digest, nb[i]); + crypto_shash_update(digest, pp[i], nn[i]); + } + ret = 0; + + kleave(" = %d", ret); + return ret; +} + +/* + * Calculate the public key ID fingerprint + */ +static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, + struct pgp_parse_pubkey *pgp, + struct public_key *pub) +{ + struct crypto_shash *tfm; + struct shash_desc *digest; + char *fingerprint; + u8 *raw_fingerprint; + int digest_size, offset; + int ret, i; + + ret = -ENOMEM; + tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ? + "md5" : "sha1", 0, 0); + if (!tfm) + goto cleanup; + + digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!digest) + goto cleanup_tfm; + + digest->tfm = tfm; + digest->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + ret = crypto_shash_init(digest); + if (ret < 0) + goto cleanup_hash; + + ret = pgp_calc_pkey_keyid(digest, pgp, pub); + if (ret < 0) + goto cleanup_hash; + + digest_size = crypto_shash_digestsize(tfm); + + raw_fingerprint = kmalloc(digest_size, GFP_KERNEL); + if (!raw_fingerprint) + goto cleanup_hash; + + ret = crypto_shash_final(digest, raw_fingerprint); + if (ret < 0) + goto cleanup_raw_fingerprint; + + fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); + if (!fingerprint) + goto cleanup_raw_fingerprint; + + offset = digest_size - 8; + pr_debug("offset %u/%u\n", offset, digest_size); + + for (i = 0; i < digest_size; i++) + sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]); + pr_debug("fingerprint %s\n", fingerprint); + + ctx->raw_fingerprint = raw_fingerprint; + ctx->fingerprint = fingerprint; + ret = 0; +cleanup_raw_fingerprint: + if (ret < 0) + kfree(raw_fingerprint); +cleanup_hash: + kfree(digest); +cleanup_tfm: + crypto_free_shash(tfm); +cleanup: + kleave(" = %d", ret); + return ret; +} + +/* + * Extract a public key or public subkey from the PGP stream. + */ +static int pgp_process_public_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, + u8 headerlen, + const u8 *data, + size_t datalen) +{ + const char *algo; + struct pgp_key_data_parse_context *ctx = + container_of(context, struct pgp_key_data_parse_context, pgp); + struct pgp_parse_pubkey pgp; + struct public_key *pub; + int ret; + + kenter(",%u,%u,,%zu", type, headerlen, datalen); + + if (ctx->fingerprint) { + kleave(" = -ENOKEY [already]"); + return -EBADMSG; + } + + pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); + if (!pub) + return -ENOMEM; + pub->id_type = "PGP"; + + ret = pgp_parse_public_key(&data, &datalen, &pgp); + if (ret < 0) + goto cleanup; + + if (pgp.pubkey_algo >= PGP_PUBKEY__LAST) + goto cleanup_unsupported_pkey_algo; + algo = pgp_to_public_key_algo[pgp.pubkey_algo]; + if (!algo) + goto cleanup_unsupported_pkey_algo; + pub->pkey_algo = algo; + + pub->key = kmemdup(data, datalen, GFP_KERNEL); + if (!pub->key) + goto cleanup_nomem; + + pub->keylen = datalen; + + ret = pgp_generate_fingerprint(ctx, &pgp, pub); + if (ret < 0) + goto cleanup; + + ctx->pub = pub; + kleave(" = 0 [use]"); + return 0; + +cleanup_unsupported_pkey_algo: + pr_debug("Unsupported public key algorithm %u\n", + pgp.pubkey_algo); + ret = -ENOPKG; + goto cleanup; +cleanup_nomem: + ret = -ENOMEM; + goto cleanup; +cleanup: + pr_devel("cleanup"); + kfree(pub->key); + kfree(pub); + kleave(" = %d", ret); + return ret; +} + +static struct asymmetric_key_ids *pgp_key_generate_id( + struct pgp_key_data_parse_context *ctx) +{ + struct asymmetric_key_ids *kids; + struct asymmetric_key_id *kid; + int fingerprint_len = strlen(ctx->fingerprint) / 2; + + kids = kzalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL); + if (!kids) + return kids; + + kid = asymmetric_key_generate_id(ctx->raw_fingerprint, fingerprint_len, + NULL, 0); + if (IS_ERR(kid)) + goto error; + + kids->id[0] = kid; + kids->id[1] = kmemdup(kid, sizeof(kid) + fingerprint_len, GFP_KERNEL); + if (!kids->id[1]) + goto error; + + return kids; +error: + kfree(kids->id[0]); + kfree(kids); + + return NULL; +} + +/* + * Attempt to parse the instantiation data blob for a key as a PGP packet + * message holding a key. + */ +static int pgp_key_parse(struct key_preparsed_payload *prep) +{ + struct pgp_key_data_parse_context ctx; + int ret; + + kenter(""); + + memset(&ctx, 0, sizeof(ctx)); + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.process_packet = pgp_process_public_key; + + ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp); + if (ret < 0) + goto error; + + /* We're pinning the module by being linked against it */ + __module_get(public_key_subtype.owner); + prep->payload.data[asym_subtype] = &public_key_subtype; + prep->payload.data[asym_key_ids] = pgp_key_generate_id(&ctx); + prep->payload.data[asym_crypto] = ctx.pub; + prep->quotalen = 100; + kfree(ctx.fingerprint); + kfree(ctx.raw_fingerprint); + return 0; + +error: + public_key_free(ctx.pub); + kfree(ctx.fingerprint); + kfree(ctx.raw_fingerprint); + return ret; +} + +static struct asymmetric_key_parser pgp_key_parser = { + .owner = THIS_MODULE, + .name = "pgp", + .parse = pgp_key_parse, +}; + +/* + * Module stuff + */ +static int __init pgp_key_init(void) +{ + return register_asymmetric_key_parser(&pgp_key_parser); +} + +static void __exit pgp_key_exit(void) +{ + unregister_asymmetric_key_parser(&pgp_key_parser); +} + +module_init(pgp_key_init); +module_exit(pgp_key_exit);
From: David Howells dhowells@redhat.com
hulk inclusion category: feature feature: digest-lists
---------------------------
Provide a facility to autogenerate the name of PGP keys from the contents of the payload. If add_key() is given a blank description, a description is constructed from the last user ID packet in the payload data plus the last 8 hex digits of the key ID. For instance:
keyctl padd asymmetric "" @s </tmp/key.pub
might create a key with a constructed description that can be seen in /proc/keys:
2f674b96 I--Q--- 1 perm 39390000 0 0 crypto \ Sample kernel key 31f0ae93: PGP.RSA 31f0ae93 []
Changelog
v0: - fix style issues (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- crypto/asymmetric_keys/pgp_public_key.c | 46 ++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c index 2845b5d1a765..8693c33c6115 100644 --- a/crypto/asymmetric_keys/pgp_public_key.c +++ b/crypto/asymmetric_keys/pgp_public_key.c @@ -36,6 +36,9 @@ struct pgp_key_data_parse_context { struct public_key *pub; unsigned char *raw_fingerprint; char *fingerprint; + const char *user_id; + size_t user_id_len; + size_t fingerprint_len; };
/* @@ -156,6 +159,7 @@ static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx, if (ret < 0) goto cleanup_raw_fingerprint;
+ ctx->fingerprint_len = digest_size * 2; fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL); if (!fingerprint) goto cleanup_raw_fingerprint; @@ -200,6 +204,13 @@ static int pgp_process_public_key(struct pgp_parse_context *context,
kenter(",%u,%u,,%zu", type, headerlen, datalen);
+ if (type == PGP_PKT_USER_ID) { + ctx->user_id = data; + ctx->user_id_len = datalen; + kleave(" = 0 [user ID]"); + return 0; + } + if (ctx->fingerprint) { kleave(" = -ENOKEY [already]"); return -EBADMSG; @@ -292,13 +303,46 @@ static int pgp_key_parse(struct key_preparsed_payload *prep) kenter("");
memset(&ctx, 0, sizeof(ctx)); - ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY) | + (1 << PGP_PKT_USER_ID); ctx.pgp.process_packet = pgp_process_public_key;
ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp); if (ret < 0) goto error;
+ if (ctx.user_id && ctx.user_id_len > 0) { + /* Propose a description for the key + * (user ID without the comment) + */ + size_t ulen = ctx.user_id_len, flen = ctx.fingerprint_len; + const char *p; + + p = memchr(ctx.user_id, '(', ulen); + if (p) { + /* Remove the comment */ + do { + p--; + } while (*p == ' ' && p > ctx.user_id); + if (*p != ' ') + p++; + ulen = p - ctx.user_id; + } + + if (ulen > 255 - 9) + ulen = 255 - 9; + prep->description = kmalloc(ulen + 1 + 8 + 1, GFP_KERNEL); + ret = -ENOMEM; + if (!prep->description) + goto error; + memcpy(prep->description, ctx.user_id, ulen); + prep->description[ulen] = ' '; + memcpy(prep->description + ulen + 1, + ctx.fingerprint + flen - 8, 8); + prep->description[ulen + 9] = 0; + pr_debug("desc '%s'\n", prep->description); + } + /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); prep->payload.data[asym_subtype] = &public_key_subtype;
hulk inclusion category: feature feature: digest-lists
---------------------------
Provide a function to load keys from a PGP keyring blob for use in initialising the module signing key keyring:
int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, struct key *keyring);
Descriptions are generated from user ID notes and key fingerprints. The keys will actually be identified by the ID calculated from the PGP data rather than by the description, so this shouldn't be a problem.
The keys are attached to the keyring supplied.
Looking as root in /proc/keys after the module signing keyring has been loaded:
383a00c1 I------ 1 perm 1f030000 0 0 asymmetri \ Red Hat, Inc. dbeca166: PGP.DSA dbeca166 []
Thanks to Tetsuo Handa penguin-kernel@I-love.SAKURA.ne.jp for some pointing out some errors.
Changelog
v0: - avoid Kconfig circular dependency (Roberto Sassu) - modify flags passed to key_create_or_update() (Roberto Sassu) - don't process Public-Subkey packets (Roberto Sassu)
Signed-off-by: David Howells dhowells@redhat.com Co-developed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- crypto/asymmetric_keys/Kconfig | 8 ++ crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pgp_preload.c | 119 +++++++++++++++++++++++++++ include/linux/pgp.h | 4 + 4 files changed, 132 insertions(+) create mode 100644 crypto/asymmetric_keys/pgp_preload.c
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 8ba0e6ac084f..180c07d1ef5b 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -82,4 +82,12 @@ config PGP_KEY_PARSER for key data and provides the ability to instantiate a crypto key from a public key packet found inside the blob.
+config PGP_PRELOAD + bool "PGP public key preloading facility" + select PGP_KEY_PARSER + help + This option provides a facility for the kernel to preload PGP-wrapped + bundles of keys during boot. It is used by module signing to load + the module signing keys for example. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 6a5256f64723..bc61fc0cf46e 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -66,6 +66,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h # PGP handling # obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o +obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o
obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o pgp_key_parser-y := \ diff --git a/crypto/asymmetric_keys/pgp_preload.c b/crypto/asymmetric_keys/pgp_preload.c new file mode 100644 index 000000000000..d029caec3601 --- /dev/null +++ b/crypto/asymmetric_keys/pgp_preload.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Cryptographic key request handling + * + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * See Documentation/security/keys-crypto.txt + */ + +#include <linux/module.h> +#include <linux/key.h> +#include <linux/pgplib.h> +#include <linux/pgp.h> +#include <linux/err.h> +#include <keys/asymmetric-type.h> + +struct preload_pgp_keys_context { + struct pgp_parse_context pgp; + key_ref_t keyring; + const u8 *key_start; + const u8 *key_end; + bool found_key; +}; + +/* + * Create a key. + */ +static int __init create_pgp_key(struct preload_pgp_keys_context *ctx) +{ + key_ref_t key; + + key = key_create_or_update(ctx->keyring, + "asymmetric", + NULL, + ctx->key_start, + ctx->key_end - ctx->key_start, + ((KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ), + KEY_ALLOC_NOT_IN_QUOTA | + KEY_ALLOC_BUILT_IN | + KEY_ALLOC_BYPASS_RESTRICTION); + if (IS_ERR(key)) + return PTR_ERR(key); + + pr_notice("Loaded PGP key '%s'\n", + key_ref_to_ptr(key)->description); + + key_ref_put(key); + return 0; +} + +/* + * Extract a public key or subkey from the PGP stream. + */ +static int __init found_pgp_key(struct pgp_parse_context *context, + enum pgp_packet_tag type, u8 headerlen, + const u8 *data, size_t datalen) +{ + struct preload_pgp_keys_context *ctx = + container_of(context, struct preload_pgp_keys_context, pgp); + int ret; + + if (ctx->found_key) { + ctx->key_end = data - headerlen; + ret = create_pgp_key(ctx); + if (ret < 0) + return ret; + } + + ctx->key_start = data - headerlen; + ctx->found_key = true; + return 0; +} + +/** + * preload_pgp_keys - Load keys from a PGP keyring blob + * @pgpdata: The PGP keyring blob containing the keys. + * @pgpdatalen: The size of the @pgpdata blob. + * @keyring: The keyring to add the new keys to. + * + * Preload a pack of keys from a PGP keyring blob. + * + * The keys have their descriptions generated from the user ID and fingerprint + * in the PGP stream. Since keys can be matched on their key IDs independently + * of the key description, the description is mostly irrelevant apart from the + * fact that keys of the same description displace one another from a keyring. + * + * The caller should override the current creds if they want the keys to be + * owned by someone other than the current process's owner. Keys will not be + * accounted towards the owner's quota. + * + * This function may only be called whilst the kernel is booting. + */ +int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring) +{ + struct preload_pgp_keys_context ctx; + int ret; + + ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY); + ctx.pgp.process_packet = found_pgp_key; + ctx.keyring = make_key_ref(keyring, 1); + ctx.found_key = false; + + ret = pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp); + if (ret < 0) + return ret; + + if (ctx.found_key) { + ctx.key_end = pgpdata + pgpdatalen; + return create_pgp_key(&ctx); + } + return 0; +} diff --git a/include/linux/pgp.h b/include/linux/pgp.h index 71186ac3128b..9104939c4770 100644 --- a/include/linux/pgp.h +++ b/include/linux/pgp.h @@ -14,6 +14,7 @@ #define _LINUX_PGP_H
#include <linux/types.h> +#include <linux/key.h>
struct pgp_key_ID { u8 id[8]; @@ -213,4 +214,7 @@ enum pgp_literal_data_format { PGP_LIT_FORMAT_TEXT_UTF8 = 0x75, };
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen, + struct key *keyring); + #endif /* _LINUX_PGP_H */
hulk inclusion category: feature feature: digest-lists
---------------------------
Preload PGP keys from 'pubring.gpg', placed in certs/ of the kernel source directory.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- certs/Kconfig | 7 +++++++ certs/Makefile | 6 ++++++ certs/system_certificates.S | 18 ++++++++++++++++++ certs/system_keyring.c | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+)
diff --git a/certs/Kconfig b/certs/Kconfig index c94e93d8bccf..71be583e55a4 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -83,4 +83,11 @@ config SYSTEM_BLACKLIST_HASH_LIST wrapper to incorporate the list into the kernel. Each <hash> should be a string of hex digits.
+config PGP_PRELOAD_PUBLIC_KEYS + bool "Preload PGP public keys" + select PGP_PRELOAD + default n + help + Provide a keyring of PGP public keys. + endmenu diff --git a/certs/Makefile b/certs/Makefile index 5d0999b9e21b..5053e3c86c97 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -4,6 +4,12 @@ #
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o +ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +ifneq ($(shell ls certs/pubring.gpg 2> /dev/null), certs/pubring.gpg) +$(shell touch certs/pubring.gpg) +endif +$(obj)/system_certificates.o: certs/pubring.gpg +endif obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"") obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o diff --git a/certs/system_certificates.S b/certs/system_certificates.S index 8f29058adf93..bcb7c4b4cc36 100644 --- a/certs/system_certificates.S +++ b/certs/system_certificates.S @@ -35,3 +35,21 @@ system_certificate_list_size: #else .long __cert_list_end - __cert_list_start #endif + + .align 8 + .globl pgp_public_keys +pgp_public_keys: +__pgp_key_list_start: +#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS + .incbin "certs/pubring.gpg" +#endif +__pgp_key_list_end: + + .align 8 + .globl pgp_public_keys_size +pgp_public_keys_size: +#ifdef CONFIG_64BIT + .quad __pgp_key_list_end - __pgp_key_list_start +#else + .long __pgp_key_list_end - __pgp_key_list_start +#endif diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 81728717523d..bf118f34dc5c 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -15,6 +15,7 @@ #include <linux/cred.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/pgp.h> #include <linux/verification.h> #include <keys/asymmetric-type.h> #include <keys/system_keyring.h> @@ -188,6 +189,27 @@ static __init int load_system_certificate_list(void) } late_initcall(load_system_certificate_list);
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS +extern __initconst const u8 pgp_public_keys[]; +extern __initconst const unsigned long pgp_public_keys_size; + +/* + * Load a list of PGP keys. + */ +static __init int load_pgp_public_keyring(void) +{ + pr_notice("Load PGP public keys\n"); + + if (preload_pgp_keys(pgp_public_keys, + pgp_public_keys_size, + builtin_trusted_keys) < 0) + pr_err("Can't load PGP public keys\n"); + + return 0; +} +late_initcall(load_pgp_public_keyring); +#endif /* CONFIG_PGP_PRELOAD_PUBLIC_KEYS */ + #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
/**
hulk inclusion category: feature feature: digest-lists
---------------------------
Introduce search_trusted_key() to extend the key search to the primary or secondary built-in keyrings.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- certs/system_keyring.c | 22 ++++++++++++++++++++++ include/linux/verification.h | 5 +++++ 2 files changed, 27 insertions(+)
diff --git a/certs/system_keyring.c b/certs/system_keyring.c index bf118f34dc5c..33ddd8ce1c97 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -287,4 +287,26 @@ int verify_pkcs7_signature(const void *data, size_t len, } EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
+struct key *search_trusted_key(struct key *trusted_keys, struct key_type *type, + char *name) +{ + key_ref_t kref; + + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == VERIFY_USE_SECONDARY_KEYRING) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } + kref = keyring_search(make_key_ref(trusted_keys, 1), type, name); + if (IS_ERR(kref)) + return ERR_CAST(kref); + + return key_ref_to_ptr(kref); +} +EXPORT_SYMBOL_GPL(search_trusted_key); + #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ diff --git a/include/linux/verification.h b/include/linux/verification.h index cfa4730d607a..6aaf06c83daa 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -12,6 +12,8 @@ #ifndef _LINUX_VERIFICATION_H #define _LINUX_VERIFICATION_H
+#include <linux/key.h> + /* * Indicate that both builtin trusted keys and secondary trusted keys * should be used. @@ -51,5 +53,8 @@ extern int verify_pefile_signature(const void *pebuf, unsigned pelen, enum key_being_used_for usage); #endif
+struct key *search_trusted_key(struct key *trusted_keys, struct key_type *type, + char *name); + #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ #endif /* _LINUX_VERIFY_PEFILE_H */
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch calls search_trusted_key() in request_asymmetric_key() if the key is not found in the IMA/EVM keyrings.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/digsig_asymmetric.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 6dc075144508..5da9e624b747 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/ratelimit.h> #include <linux/key-type.h> +#include <linux/verification.h> #include <crypto/public_key.h> #include <crypto/hash_info.h> #include <keys/asymmetric-type.h> @@ -60,6 +61,15 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key = request_key(&key_type_asymmetric, name, NULL); }
+ if (IS_ERR(key)) { +#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY + keyring = VERIFY_USE_SECONDARY_KEYRING; +#else + keyring = NULL; +#endif + key = search_trusted_key(keyring, &key_type_asymmetric, name); + } + if (IS_ERR(key)) { pr_err_ratelimited("Request for unknown key '%s' err %ld\n", name, PTR_ERR(key));
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch allows direct upload of digest lists by user space parsers. This operation is possible if the digest of the process's executable is found in the digest lists and its type is COMPACT_PARSER.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_digest_list.c | 39 ++++++++++++++++++++++++ security/integrity/ima/ima_digest_list.h | 5 +++ security/integrity/ima/ima_fs.c | 12 ++++++++ 3 files changed, 56 insertions(+)
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index c7610d9d603c..fbcc3d8a58a6 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -19,6 +19,8 @@ #include <linux/module.h> #include <linux/file.h> #include <linux/namei.h> +#include <linux/sched/mm.h> +#include <linux/magic.h>
#include "ima.h" #include "ima_digest_list.h" @@ -215,6 +217,10 @@ void ima_check_measured_appraised(struct file *file) if (!ima_digest_list_actions) return;
+ if (file_inode(file)->i_sb->s_magic == SECURITYFS_MAGIC || + S_ISDIR(file_inode(file)->i_mode)) + return; + iint = integrity_iint_find(file_inode(file)); if (!iint) { pr_err("%s not processed, disabling digest lists lookup\n", @@ -322,3 +328,36 @@ void __init ima_load_digest_lists(void) out: path_put(&path); } + +/**************** + * Parser check * + ****************/ +bool ima_check_current_is_parser(void) +{ + struct integrity_iint_cache *parser_iint; + struct file *parser_file; + struct mm_struct *mm; + + mm = get_task_mm(current); + if (!mm) + return false; + + parser_file = get_mm_exe_file(mm); + mmput(mm); + + if (!parser_file) + return false; + + parser_iint = integrity_iint_find(file_inode(parser_file)); + fput(parser_file); + + if (!parser_iint) + return false; + + /* flag cannot be cleared due to write protection of executables */ + if (!(parser_iint->flags & IMA_COLLECTED)) + return false; + + return ima_lookup_digest(parser_iint->ima_hash->digest, + parser_iint->ima_hash->algo, COMPACT_PARSER); +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h index f587e9ab8f75..f102530324a5 100644 --- a/security/integrity/ima/ima_digest_list.h +++ b/security/integrity/ima/ima_digest_list.h @@ -24,6 +24,7 @@ extern struct ima_h_table ima_digests_htable;
int ima_parse_compact_list(loff_t size, void *buf, int op); void ima_check_measured_appraised(struct file *file); +bool ima_check_current_is_parser(void); #else static inline int ima_parse_compact_list(loff_t size, void *buf, int op) { @@ -32,5 +33,9 @@ static inline int ima_parse_compact_list(loff_t size, void *buf, int op) static inline void ima_check_measured_appraised(struct file *file) { } +static inline bool ima_check_current_is_parser(void) +{ + return false; +} #endif /*CONFIG_IMA_DIGEST_LIST*/ #endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 895e242066ac..804e7183a908 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -392,6 +392,18 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, } else { result = ima_parse_add_rule(data); } + } else if (dentry == digest_list_data) { + if (!ima_check_current_is_parser()) + result = -EACCES; + else + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_ADD); + } else if (dentry == digest_list_data_del) { + if (!ima_check_current_is_parser()) + result = -EACCES; + else + result = ima_parse_compact_list(datalen, data, + DIGEST_LIST_OP_DEL); } else { pr_err("Unknown data type\n"); result = -EINVAL;
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch adds the new policy keyword parser to measure and appraise any file opened by the user space parser, while the parser opened digest_list_data_add or digest_list_data_del securityfs interfaces.
This ensures that all files processed by the user space parser are processed without including the FILE_CHECK hook in the policy. With this keyword it would be possible to have a policy to measure/appraise only executable code and digest lists.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_digest_list.c | 17 +++++++++++++ security/integrity/ima/ima_digest_list.h | 13 ++++++++++ security/integrity/ima/ima_fs.c | 15 ++++++++--- security/integrity/ima/ima_policy.c | 32 +++++++++++++++++++++++- 5 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 78b7f2fcd7d8..52d2b3861b86 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -247,7 +247,7 @@ int ima_appraise_measurement(enum ima_hooks func, /* Allow access to digest lists without metadata, only if they * are signed or found in a digest list (immutable) */ - if (func == DIGEST_LIST_CHECK) { + if (func == DIGEST_LIST_CHECK || ima_current_is_parser()) { if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) break; if (found_digest && diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index fbcc3d8a58a6..11d00fe63ffb 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -361,3 +361,20 @@ bool ima_check_current_is_parser(void) return ima_lookup_digest(parser_iint->ima_hash->digest, parser_iint->ima_hash->algo, COMPACT_PARSER); } + +struct task_struct *parser_task; + +void ima_set_parser(void) +{ + parser_task = current; +} + +void ima_unset_parser(void) +{ + parser_task = NULL; +} + +bool ima_current_is_parser(void) +{ + return (current == parser_task); +} diff --git a/security/integrity/ima/ima_digest_list.h b/security/integrity/ima/ima_digest_list.h index f102530324a5..a47b2c6ee8de 100644 --- a/security/integrity/ima/ima_digest_list.h +++ b/security/integrity/ima/ima_digest_list.h @@ -25,6 +25,9 @@ extern struct ima_h_table ima_digests_htable; int ima_parse_compact_list(loff_t size, void *buf, int op); void ima_check_measured_appraised(struct file *file); bool ima_check_current_is_parser(void); +void ima_set_parser(void); +void ima_unset_parser(void); +bool ima_current_is_parser(void); #else static inline int ima_parse_compact_list(loff_t size, void *buf, int op) { @@ -37,5 +40,15 @@ static inline bool ima_check_current_is_parser(void) { return false; } +static inline void ima_set_parser(void) +{ +} +static inline void ima_unset_parser(void) +{ +} +static inline bool ima_current_is_parser(void) +{ + return false; +} #endif /*CONFIG_IMA_DIGEST_LIST*/ #endif /*LINUX_IMA_DIGEST_LIST_H*/ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 804e7183a908..8c28ff907aa7 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -393,17 +393,21 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, result = ima_parse_add_rule(data); } } else if (dentry == digest_list_data) { - if (!ima_check_current_is_parser()) + if (!ima_check_current_is_parser()) { result = -EACCES; - else + } else { + ima_set_parser(); result = ima_parse_compact_list(datalen, data, DIGEST_LIST_OP_ADD); + } } else if (dentry == digest_list_data_del) { - if (!ima_check_current_is_parser()) + if (!ima_check_current_is_parser()) { result = -EACCES; - else + } else { + ima_set_parser(); result = ima_parse_compact_list(datalen, data, DIGEST_LIST_OP_DEL); + } } else { pr_err("Unknown data type\n"); result = -EINVAL; @@ -494,6 +498,9 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file);
+ if (dentry == digest_list_data || dentry == digest_list_data_del) + ima_unset_parser(); + if (dentry != ima_policy) { clear_bit(flag, &ima_fs_flags); return 0; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index fa5ce0de932b..c5b6d11e534f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -22,6 +22,7 @@ #include <linux/seq_file.h>
#include "ima.h" +#include "ima_digest_list.h"
/* flags definitions */ #define IMA_FUNC 0x0001 @@ -34,6 +35,7 @@ #define IMA_EUID 0x0080 #define IMA_PCR 0x0100 #define IMA_FSNAME 0x0200 +#define IMA_PARSER 0x0400
#define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -136,6 +138,10 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, };
+static struct ima_rule_entry ima_parser_measure_rule __ro_after_init = { + .action = MEASURE, .flags = IMA_PARSER +}; + static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, @@ -203,6 +209,11 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, };
+static struct ima_rule_entry ima_parser_appraise_rule __ro_after_init = { + .action = APPRAISE, + .flags = IMA_PARSER | IMA_DIGSIG_REQUIRED +}; + static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_policy_rules); static LIST_HEAD(ima_temp_rules); @@ -334,6 +345,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, if ((rule->flags & IMA_FOWNER) && !rule->fowner_op(inode->i_uid, rule->fowner)) return false; + if ((rule->flags & IMA_PARSER) && + !ima_current_is_parser()) + return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; @@ -546,6 +560,10 @@ void __init ima_init_policy(void) break; }
+ if (ima_policy) + list_add_tail(&ima_parser_measure_rule.list, + &ima_default_rules); + /* * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to any other appraise rules. @@ -599,6 +617,11 @@ void __init ima_init_policy(void) &ima_default_rules); }
+ if (ima_use_secure_boot || ima_use_appraise_tcb || + ima_use_appraise_exec_tcb) + list_add_tail(&ima_parser_appraise_rule.list, + &ima_default_rules); + ima_update_policy_flag(); }
@@ -646,7 +669,7 @@ enum { Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, Opt_appraise_type, Opt_permit_directio, - Opt_pcr + Opt_pcr, Opt_parser };
static match_table_t policy_tokens = { @@ -680,6 +703,7 @@ static match_table_t policy_tokens = { {Opt_appraise_type, "appraise_type=%s"}, {Opt_permit_directio, "permit_directio"}, {Opt_pcr, "pcr=%s"}, + {Opt_parser, "parser"}, {Opt_err, NULL} };
@@ -1029,6 +1053,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else entry->flags |= IMA_PCR;
+ break; + case Opt_parser: + entry->flags |= IMA_PARSER; break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); @@ -1275,6 +1302,9 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); }
+ if (entry->flags & IMA_PARSER) + seq_puts(m, "parser "); + for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].rule) { switch (i) {
hulk inclusion category: feature feature: digest-lists
---------------------------
This patch limits the digest lists processed by the kernel. It excludes digest lists not in the compact format, and those without security.evm or without IMA signature.
ima_check_measured_appraised() is called at the end of ima_file_check() to verify that everything accessed by the user space parser (except for directories and securityfs) has been processed by IMA. If a digest list was not processed by an IMA submodule, digest list lookup is disabled for that submodule.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- security/integrity/ima/Kconfig | 7 ++++ security/integrity/ima/ima_digest_list.c | 41 ++++++++++++++++++++++++ security/integrity/ima/ima_main.c | 10 ++++-- 3 files changed, 55 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 5fe5f67b2e18..00486c2d7090 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -305,3 +305,10 @@ config IMA_DIGEST_LISTS_DIR help This option defines the path of the directory containing digest lists. + +config IMA_PARSER_BINARY_PATH + string "Path of the parser binary" + depends on IMA_DIGEST_LIST + default "/usr/bin/upload_digest_lists" + help + This option defines the path of the parser binary. diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 11d00fe63ffb..cbb55d58cdb2 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/file.h> #include <linux/namei.h> +#include <linux/xattr.h> #include <linux/sched/mm.h> #include <linux/magic.h>
@@ -271,6 +272,8 @@ static int __init load_digest_list(struct dir_context *__ctx, const char *name, struct readdir_callback *ctx = container_of(__ctx, typeof(*ctx), ctx); struct path *dir = ctx->path; struct file *file; + u8 *xattr_value = NULL; + char *type_start, *format_start, *format_end; void *datap; loff_t size; int ret; @@ -278,12 +281,36 @@ static int __init load_digest_list(struct dir_context *__ctx, const char *name, if (!strcmp(name, ".") || !strcmp(name, "..")) return 0;
+ type_start = strchr(name, '-'); + if (!type_start) + return 0; + + format_start = strchr(type_start + 1, '-'); + if (!format_start) + return 0; + + format_end = strchr(format_start + 1, '-'); + if (!format_end) + return 0; + + if (format_end - format_start - 1 != strlen("compact") || + strncmp(format_start + 1, "compact", format_end - format_start - 1)) + return 0; + file = file_open_root(dir->dentry, dir->mnt, name, O_RDONLY, 0); if (IS_ERR(file)) { pr_err("Unable to open file: %s (%ld)", name, PTR_ERR(file)); return 0; }
+ size = vfs_getxattr(file_dentry(file), XATTR_NAME_EVM, NULL, 0); + if (size < 0) { + size = vfs_getxattr_alloc(file_dentry(file), XATTR_NAME_IMA, + (char **)&xattr_value, 0, GFP_NOFS); + if (size < 0 || xattr_value[0] != EVM_IMA_XATTR_DIGSIG) + goto out; + } + ret = kernel_read_file(file, &datap, &size, 0, READING_DIGEST_LIST); if (ret < 0) { pr_err("Unable to read file: %s (%d)", name, ret); @@ -299,9 +326,21 @@ static int __init load_digest_list(struct dir_context *__ctx, const char *name, vfree(datap); out: fput(file); + kfree(xattr_value); return 0; }
+static void ima_exec_parser(void) +{ + char *argv[4] = {NULL}, *envp[1] = {NULL}; + + argv[0] = (char *)CONFIG_IMA_PARSER_BINARY_PATH; + argv[1] = "add"; + argv[2] = (char *)CONFIG_IMA_DIGEST_LISTS_DIR; + + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); +} + void __init ima_load_digest_lists(void) { struct path path; @@ -327,6 +366,8 @@ void __init ima_load_digest_lists(void) fput(file); out: path_put(&path); + + ima_exec_parser(); }
/**************** diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index a386a5bf1ed9..4875c6db7793 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -460,11 +460,15 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask) { u32 secid; + int rc;
security_task_getsecid(current, &secid); - return process_measurement(file, current_cred(), secid, NULL, 0, - mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); + rc = process_measurement(file, current_cred(), secid, NULL, 0, + mask & (MAY_READ | MAY_WRITE | MAY_EXEC | + MAY_APPEND), FILE_CHECK); + if (ima_current_is_parser() && !rc) + ima_check_measured_appraised(file); + return rc; } EXPORT_SYMBOL_GPL(ima_file_check);
hulk inclusion category: feature feature: digest-lists
---------------------------
Introduce two new values for evm= kernel option:
x509: enable EVM by setting x509 flag; allow_metadata_writes: permit metadata modificatons.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- Documentation/admin-guide/kernel-parameters.txt | 8 +++++--- security/integrity/evm/evm_main.c | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 14fd02ce8367..a36f9b47c0be 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1212,9 +1212,11 @@ has equivalent usage. See its documentation for details.
evm= [EVM] - Format: { "fix" } - Permit 'security.evm' to be updated regardless of - current integrity status. + Format: { "fix" | "x509" | "allow_metadata_writes" } + fix: permit 'security.evm' to be updated regardless of + current integrity status; + x509: enable EVM by setting x509 flag; + allow_metadata_writes: permit metadata modificatons.
failslab= fail_page_alloc= diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index aae1cb7603ac..138c0ab419fa 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -62,13 +62,17 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames);
static int evm_fixmode; -static int __init evm_set_fixmode(char *str) +static int __init evm_set_param(char *str) { if (strncmp(str, "fix", 3) == 0) evm_fixmode = 1; + else if (strncmp(str, "x509", 4) == 0) + evm_initialized |= EVM_INIT_X509; + else if (strncmp(str, "allow_metadata_writes", 21) == 0) + evm_initialized |= EVM_ALLOW_METADATA_WRITES; return 0; } -__setup("evm=", evm_set_fixmode); +__setup("evm=", evm_set_param);
static void __init evm_init_config(void) {
hulk inclusion category: feature feature: digest-lists
---------------------------
Enable digest lists and PGP keys preload.
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com --- arch/x86/configs/openeuler_defconfig | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 37c7c8893e3b..9cc2ec703fb4 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -164,6 +164,7 @@ CONFIG_RD_LZMA=y CONFIG_RD_XZ=y CONFIG_RD_LZO=y CONFIG_RD_LZ4=y +CONFIG_INITRAMFS_FILE_METADATA="" CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y @@ -6927,21 +6928,27 @@ CONFIG_IMA_LSM_RULES=y CONFIG_IMA_NG_TEMPLATE=y # CONFIG_IMA_SIG_TEMPLATE is not set CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng" -CONFIG_IMA_DEFAULT_HASH_SHA1=y -# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set -CONFIG_IMA_DEFAULT_HASH="sha1" +# CONFIG_IMA_DEFAULT_HASH_SHA1 is not set +CONFIG_IMA_DEFAULT_HASH_SHA256=y +CONFIG_IMA_DEFAULT_HASH="sha256" # CONFIG_IMA_WRITE_POLICY is not set -# CONFIG_IMA_READ_POLICY is not set +CONFIG_IMA_READ_POLICY=y CONFIG_IMA_APPRAISE=y # CONFIG_IMA_APPRAISE_BUILD_POLICY is not set CONFIG_IMA_APPRAISE_BOOTPARAM=y CONFIG_IMA_TRUSTED_KEYRING=y # CONFIG_IMA_BLACKLIST_KEYRING is not set -# CONFIG_IMA_LOAD_X509 is not set +CONFIG_IMA_LOAD_X509=y +CONFIG_IMA_X509_PATH="/etc/keys/x509_ima.der" +# CONFIG_IMA_APPRAISE_SIGNED_INIT is not set +CONFIG_IMA_DIGEST_LIST=y +CONFIG_IMA_DIGEST_LISTS_DIR="/etc/ima/digest_lists" +CONFIG_IMA_PARSER_BINARY_PATH="/usr/bin/upload_digest_lists" CONFIG_EVM=y CONFIG_EVM_ATTR_FSUUID=y # CONFIG_EVM_ADD_XATTRS is not set -# CONFIG_EVM_LOAD_X509 is not set +CONFIG_EVM_LOAD_X509=y +CONFIG_EVM_X509_PATH="/etc/keys/x509_evm.der" CONFIG_DEFAULT_SECURITY_SELINUX=y # CONFIG_DEFAULT_SECURITY_APPARMOR is not set # CONFIG_DEFAULT_SECURITY_DAC is not set @@ -7159,6 +7166,9 @@ CONFIG_X509_CERTIFICATE_PARSER=y CONFIG_PKCS7_MESSAGE_PARSER=y # CONFIG_PKCS7_TEST_KEY is not set CONFIG_SIGNED_PE_FILE_VERIFICATION=y +CONFIG_PGP_LIBRARY=y +CONFIG_PGP_KEY_PARSER=y +CONFIG_PGP_PRELOAD=y
# # Certificates for signature checking @@ -7170,6 +7180,7 @@ CONFIG_SYSTEM_TRUSTED_KEYS="" # CONFIG_SECONDARY_TRUSTED_KEYRING is not set CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" +CONFIG_PGP_PRELOAD_PUBLIC_KEYS=y CONFIG_BINARY_PRINTF=y
#
On 2020/7/28 15:40, Roberto Sassu wrote:
hulk inclusion category: feature feature: digest-lists
A fake IMA xattr is created to perform EVM verification even if security.ima is not present. Appraisal could succeed if EVM status is unknown and the file digest is found in a digest list.
This patch allocates a larger buffer to store fake IMA xattrs (struct evm_ima_xattr_data can be used only for SHA1 digests).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Hi Roberto, would you mind adding a cover letter to describe why we need this patch set? and what's the relationship with your previous IMA patch set?
Thanks Hanjun
-----Original Message----- From: Guohanjun (Hanjun Guo) Sent: Tuesday, July 28, 2020 9:58 AM To: Roberto Sassu roberto.sassu@huawei.com; kernel@openeuler.org Cc: Silviu Vlasceanu Silviu.Vlasceanu@huawei.com Subject: Re: [PATCH 01/23] ima: Use buffer large enough to store fake IMA xattr for appraisal
On 2020/7/28 15:40, Roberto Sassu wrote:
hulk inclusion category: feature feature: digest-lists
A fake IMA xattr is created to perform EVM verification even if security.ima is not present. Appraisal could succeed if EVM status is unknown and the file digest is found in a digest list.
This patch allocates a larger buffer to store fake IMA xattrs (struct evm_ima_xattr_data can be used only for SHA1 digests).
Signed-off-by: Roberto Sassu roberto.sassu@huawei.com
Hi Roberto, would you mind adding a cover letter to describe why we need this patch set? and what's the relationship with your previous IMA patch set?
Hi Hanjun
ok, will send it.
Roberto
HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Li Peng, Li Jian, Shi Yanli
Thanks Hanjun
This patch includes some bug fixes, support for PGP keys and for user space parsers of digest lists.
The first part refines the functionality of digest lists. It fixes a possible buffer overflow, relaxes the meta_immutable requirement so that digest lists without metadata can be loaded and introduces the possibility to appraise files without setting xattrs. The latest change is necessary to appraise temporary files created by the firewalld daemon which don't have xattrs.
The second part introduces support for PGP keys, to appraise digest lists when they cannot be directly signed by the build service. The public part of PGP keys from the build service are included in the kernel and used by IMA appraisal to verify RPM headers.
The third part introduces support for user space parsers of digest lists. The RPM parser extracts from the header the digest of the digest list, included in the RPM, and uploads it to the kernel so that when IMA appraisal finds that the digest is known will grant access to that digest list and let the user space parser upload remaining digests of the files in the RPM.
Lastly, the patch set introduces some minor changes. It enhances the evm= kernel option to initialize EVM and to permit metadata modifications, and enables the digest lists feature by default.
David Howells (4): PGPLIB: PGP definitions (RFC 4880) PGPLIB: Basic packet parser KEYS: PGP data parser KEYS: Provide PGP key description autogeneration
Roberto Sassu (19): ima: Use buffer large enough to store fake IMA xattr for appraisal ima: Require meta_immutable only for BPRM_CHECK hook ima: Check meta_immutable requirement for every EVM status ima: Change fake IMA xattr type to IMA_XATTR_DIGEST_NG evm: Reset status even when security.evm is modified ima: Display more information in ima_check_measured_appraised() ima: Allow appraisal of digest lists without metadata evm: Set fake EVM xattr if IMA passed a fake xattr mpi: introduce mpi_key_length() rsa: add parser of raw format KEYS: Provide a function to load keys from a PGP keyring blob KEYS: Introduce load_pgp_public_keyring() certs: Introduce search_trusted_key() ima: Search key in the built-in keyrings ima: Allow direct upload of digest lists to securityfs ima: Add parser keyword to the policy ima: Execute parser to upload digest lists not recognizable by the kernel evm: Extend evm= with x509 and allow_metadata_writes values config: Add digest lists options
.../admin-guide/kernel-parameters.txt | 8 +- arch/x86/configs/openeuler_defconfig | 23 +- certs/Kconfig | 7 + certs/Makefile | 6 + certs/system_certificates.S | 18 + certs/system_keyring.c | 44 ++ crypto/asymmetric_keys/Kconfig | 25 ++ crypto/asymmetric_keys/Makefile | 10 + crypto/asymmetric_keys/pgp_library.c | 281 +++++++++++++ crypto/asymmetric_keys/pgp_parser.h | 23 ++ crypto/asymmetric_keys/pgp_preload.c | 119 ++++++ crypto/asymmetric_keys/pgp_public_key.c | 383 ++++++++++++++++++ crypto/rsa.c | 14 +- crypto/rsa_helper.c | 69 ++++ include/crypto/internal/rsa.h | 6 + include/linux/mpi.h | 2 + include/linux/pgp.h | 220 ++++++++++ include/linux/pgplib.h | 48 +++ include/linux/verification.h | 5 + lib/mpi/mpicoder.c | 33 +- security/integrity/digsig_asymmetric.c | 10 + security/integrity/evm/evm_main.c | 58 ++- security/integrity/ima/Kconfig | 7 + security/integrity/ima/ima_appraise.c | 65 ++- security/integrity/ima/ima_digest_list.c | 106 ++++- security/integrity/ima/ima_digest_list.h | 18 + security/integrity/ima/ima_fs.c | 19 + security/integrity/ima/ima_main.c | 10 +- security/integrity/ima/ima_policy.c | 38 +- 29 files changed, 1599 insertions(+), 76 deletions(-) create mode 100644 crypto/asymmetric_keys/pgp_library.c create mode 100644 crypto/asymmetric_keys/pgp_parser.h create mode 100644 crypto/asymmetric_keys/pgp_preload.c create mode 100644 crypto/asymmetric_keys/pgp_public_key.c create mode 100644 include/linux/pgp.h create mode 100644 include/linux/pgplib.h
Applied.
On 2020/7/28 18:44, Roberto Sassu wrote:
This patch includes some bug fixes, support for PGP keys and for user space parsers of digest lists.
The first part refines the functionality of digest lists. It fixes a possible buffer overflow, relaxes the meta_immutable requirement so that digest lists without metadata can be loaded and introduces the possibility to appraise files without setting xattrs. The latest change is necessary to appraise temporary files created by the firewalld daemon which don't have xattrs.
The second part introduces support for PGP keys, to appraise digest lists when they cannot be directly signed by the build service. The public part of PGP keys from the build service are included in the kernel and used by IMA appraisal to verify RPM headers.
The third part introduces support for user space parsers of digest lists. The RPM parser extracts from the header the digest of the digest list, included in the RPM, and uploads it to the kernel so that when IMA appraisal finds that the digest is known will grant access to that digest list and let the user space parser upload remaining digests of the files in the RPM.
Lastly, the patch set introduces some minor changes. It enhances the evm= kernel option to initialize EVM and to permit metadata modifications, and enables the digest lists feature by default.
David Howells (4): PGPLIB: PGP definitions (RFC 4880) PGPLIB: Basic packet parser KEYS: PGP data parser KEYS: Provide PGP key description autogeneration
Roberto Sassu (19): ima: Use buffer large enough to store fake IMA xattr for appraisal ima: Require meta_immutable only for BPRM_CHECK hook ima: Check meta_immutable requirement for every EVM status ima: Change fake IMA xattr type to IMA_XATTR_DIGEST_NG evm: Reset status even when security.evm is modified ima: Display more information in ima_check_measured_appraised() ima: Allow appraisal of digest lists without metadata evm: Set fake EVM xattr if IMA passed a fake xattr mpi: introduce mpi_key_length() rsa: add parser of raw format KEYS: Provide a function to load keys from a PGP keyring blob KEYS: Introduce load_pgp_public_keyring() certs: Introduce search_trusted_key() ima: Search key in the built-in keyrings ima: Allow direct upload of digest lists to securityfs ima: Add parser keyword to the policy ima: Execute parser to upload digest lists not recognizable by the kernel evm: Extend evm= with x509 and allow_metadata_writes values config: Add digest lists options
.../admin-guide/kernel-parameters.txt | 8 +- arch/x86/configs/openeuler_defconfig | 23 +- certs/Kconfig | 7 + certs/Makefile | 6 + certs/system_certificates.S | 18 + certs/system_keyring.c | 44 ++ crypto/asymmetric_keys/Kconfig | 25 ++ crypto/asymmetric_keys/Makefile | 10 + crypto/asymmetric_keys/pgp_library.c | 281 +++++++++++++ crypto/asymmetric_keys/pgp_parser.h | 23 ++ crypto/asymmetric_keys/pgp_preload.c | 119 ++++++ crypto/asymmetric_keys/pgp_public_key.c | 383 ++++++++++++++++++ crypto/rsa.c | 14 +- crypto/rsa_helper.c | 69 ++++ include/crypto/internal/rsa.h | 6 + include/linux/mpi.h | 2 + include/linux/pgp.h | 220 ++++++++++ include/linux/pgplib.h | 48 +++ include/linux/verification.h | 5 + lib/mpi/mpicoder.c | 33 +- security/integrity/digsig_asymmetric.c | 10 + security/integrity/evm/evm_main.c | 58 ++- security/integrity/ima/Kconfig | 7 + security/integrity/ima/ima_appraise.c | 65 ++- security/integrity/ima/ima_digest_list.c | 106 ++++- security/integrity/ima/ima_digest_list.h | 18 + security/integrity/ima/ima_fs.c | 19 + security/integrity/ima/ima_main.c | 10 +- security/integrity/ima/ima_policy.c | 38 +- 29 files changed, 1599 insertions(+), 76 deletions(-) create mode 100644 crypto/asymmetric_keys/pgp_library.c create mode 100644 crypto/asymmetric_keys/pgp_parser.h create mode 100644 crypto/asymmetric_keys/pgp_preload.c create mode 100644 crypto/asymmetric_keys/pgp_public_key.c create mode 100644 include/linux/pgp.h create mode 100644 include/linux/pgplib.h