Re: [PATCH v2 06/15] ima: add parser of digest lists metadata
On 11/19/2017 12:23 AM, Mimi Zohar wrote: Hi Serge, On Fri, 2017-11-17 at 22:20 -0600, Serge E. Hallyn wrote: On Tue, Nov 07, 2017 at 11:37:01AM +0100, Roberto Sassu wrote: from a predefined position (/etc/ima/digest_lists/metadata), when rootfs becomes available. Digest lists must be loaded before IMA appraisal is in enforcing mode. I'm sure there's a good reason for it, but this seems weird to me. Why read it from a file on disk instead of accepting it through say a securityfile write? There are two reasons. Digest lists must be loaded before any file is accessed, otherwise IMA will deny the operation if appraisal is in enforcing mode. With digest lists it is possible to appraise files in the initial ram disk without including extended attributes (the default policy excludes those files). The second reason is that appraisal has to be temporarily disabled because the file containing digest list metadata is not signed. The same happens when loading a public key (check ima_load_x509() in ima_init.c). The file containing digest list metadata is not signed because its content depends on the list of installed packages. I thought it is acceptable to load it without verification, as providing the path of digest lists is similar to writing the path of a policy to a securityfs file. The important point is that no digest is added to the hash table without verifying the signature first. The alternative would be to load signed digest lists directly. But, the main issue is that there would be a PCR extend for each digest list, while with digest list metadata there is only one. Assuming that the concept of a white list is something we want to support, then at minimum the list needs to be signed and verified. Instead of defining a new Kconfig pathname option, a securityfs file could read it, like the IMA policy. Both methods are supported (patch 9/15 introduces 'digest_lists' in the securityfs filesystem). The securityfs file can be used to load digest lists for files not in the initial ram disk, and for system updates. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/9/2017 5:46 PM, Matthew Garrett wrote: On Thu, Nov 9, 2017 at 11:13 AM, Roberto Sassu <roberto.sa...@huawei.com> wrote: On 11/9/2017 3:47 PM, Matthew Garrett wrote: There's no need to have a policy that measures those files, because they're part of the already-measured initramfs. Just set the IMA policy after you've loaded the digest list. The default IMA policy measures files accessed from the initial ram disk. It is easier to verify individual files, rather than the whole image. That's a matter of implementation. You're not forced to use the default policy. This seems very over-complicated, and it's unclear why the kernel needs to open the file itself. You *know* that all of userland is You can have a look at ima_fs.c. If appraisal is in enforcing mode, direct upload of a policy is not permitted. The kernel reads the policy, calculates the digest, and verifies the signature. Is there an expectation that you'll load additional digest lists at runtime? Yes, before new packages are installed. trustworthy at this point even in the absence of signatures. It seem > reasonable to provide a interface that allows userland to pass a digest list to the kernel, in the same way that userland can pass an IMA policy to the kernel. You can then restrict access to that interface via an LSM. Then digest lists cannot be used alone, without an LSM. Also, verifiers have to check the LSM policy to ensure that only the parser was able to upload the digest lists. Only if you want to add additional digest lists at runtime, but yes, you really want to be verifying the LSM policy in any case. If the trustworthiness of the initial ram disk can be guaranteed with low effort, for example if the image is distributed by the vendor, it would be easier to guarantee that digest lists were uploaded by the parser. But, likely, the initial ram disk is generated by the local system. Relying on the assumption that the vendor distributes the initial ram disk would limit, in my opinion, the applicability of the digest list feature. Relying on the enforcement by an LSM would make the integrity evaluation much more complex. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/9/2017 3:47 PM, Matthew Garrett wrote: On Thu, Nov 9, 2017 at 4:51 AM, Roberto Sassu <roberto.sa...@huawei.com> wrote: On 11/8/2017 4:48 PM, Matthew Garrett wrote: The code doing the parsing is in the initramfs, which has already been measured at boot time. You can guarantee that it's being done by trusted code. The parser can be executed in the initial ram disk, but everything accessed before the parser is executed will be measured/appraised without digest lists. To do signature-based remote attestation, where the verification consists on checking the signature of digests of measured files, it would be necessary to sign systemd, libraries, everything accessed before the parser, and the parser. If RPM headers are parsed by the kernel, measurement/appraisal will be done directly with digest lists. There's no need to have a policy that measures those files, because they're part of the already-measured initramfs. Just set the IMA policy after you've loaded the digest list. The default IMA policy measures files accessed from the initial ram disk. It is easier to verify individual files, rather than the whole image. The main problem is that the digest list measurement, performed when the parser accesses the file containing the RPM header, might not reflect what IMA uses for digest lookup. Why not? I assumed you wanted to measure digest lists only at the time they are read by the parser, and not when they are read by IMA. If instead digest lists are verified again after conversion, the new workflow should be: 1) the kernel parses digest list metadata before systemd is executed 2) the kernel verifies the signature of digest lists (RPM headers) and add the digest of digest lists to the hash table, so that appraisal succeeds 3) systemd (with file signature) is executed 4) the parser (with file signature) is executed 5) the parser reads and converts the digest lists to the generic format, and writes them to a tmpfs filesystem 6) the parser generates a new digest list metadata file with the path of converted digest lists and sends the path of the new metadata to IMA 7) IMA reads the generic digest lists The measurement list should look like: 10 ima-sig boot_aggregate 10 ima-sig /etc/ima/digest_lists/metadata 10 ima-sig /usr/lib/systemd/systemd ... 10 ima-sig 10 ima-sig /tmp/metadata If parsing of RPM headers is done by the kernel, the measurement list will look like: 10 ima-ng boot_aggregate 10 ima-ng /etc/ima/digest_lists/metadata A built-in policy should enable appraisal of tmpfs. If not, patch 11/15 disables digest lookup for appraisal. Since generic digest lists will have a security.ima extended attribute (they are mutable files), appraisal verification will succeed. With this solution, digital signatures cannot be required, because generic digest lists will have a HMAC. For appraisal, it becomes necessary to ensure that only digest lists written by the parser can be processed by IMA. This seems very over-complicated, and it's unclear why the kernel needs to open the file itself. You *know* that all of userland is You can have a look at ima_fs.c. If appraisal is in enforcing mode, direct upload of a policy is not permitted. The kernel reads the policy, calculates the digest, and verifies the signature. trustworthy at this point even in the absence of signatures. It seem > reasonable to provide a interface that allows userland to pass a digest list to the kernel, in the same way that userland can pass an IMA policy to the kernel. You can then restrict access to that interface via an LSM. Then digest lists cannot be used alone, without an LSM. Also, verifiers have to check the LSM policy to ensure that only the parser was able to upload the digest lists. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/8/2017 4:48 PM, Matthew Garrett wrote: On Wed, Nov 8, 2017 at 7:00 AM, Roberto Sassu <roberto.sa...@huawei.com> wrote: On 11/7/2017 7:06 PM, Matthew Garrett wrote: But we're still left in a state where the kernel has to end up supporting a number of very niche formats, and userland agility is tied to the kernel. I think it makes significantly more sense to push the problem out to userland. At least for appraisal, digest lists must be parsed by the kernel. If the parser is moved to userspace, I don't know if we are able to provide the same guarantee, that the correct set of digests has been uploaded to IMA. A new measurement can be added, when IMA receives the digests, but a verifier has to verify the signature of the original file, perform format conversion, calculate the digest and compare it with that in the new IMA measurement. If digest lists are parsed directly by the kernel, then the signature can be verified directly. The code doing the parsing is in the initramfs, which has already been measured at boot time. You can guarantee that it's being done by trusted code. The parser can be executed in the initial ram disk, but everything accessed before the parser is executed will be measured/appraised without digest lists. To do signature-based remote attestation, where the verification consists on checking the signature of digests of measured files, it would be necessary to sign systemd, libraries, everything accessed before the parser, and the parser. If RPM headers are parsed by the kernel, measurement/appraisal will be done directly with digest lists. Isn't failing to upload the expected digest list just a DoS? We already expect to load keys from initramfs, so it seems fine to parse stuff there - what's the problem with extracting information from RPMs, translating them to the generic format and pushing that into the kernel? The main problem is that the digest list measurement, performed when the parser accesses the file containing the RPM header, might not reflect what IMA uses for digest lookup. Why not? I assumed you wanted to measure digest lists only at the time they are read by the parser, and not when they are read by IMA. If instead digest lists are verified again after conversion, the new workflow should be: 1) the kernel parses digest list metadata before systemd is executed 2) the kernel verifies the signature of digest lists (RPM headers) and add the digest of digest lists to the hash table, so that appraisal succeeds 3) systemd (with file signature) is executed 4) the parser (with file signature) is executed 5) the parser reads and converts the digest lists to the generic format, and writes them to a tmpfs filesystem 6) the parser generates a new digest list metadata file with the path of converted digest lists and sends the path of the new metadata to IMA 7) IMA reads the generic digest lists The measurement list should look like: 10 ima-sig boot_aggregate 10 ima-sig /etc/ima/digest_lists/metadata 10 ima-sig /usr/lib/systemd/systemd ... 10 ima-sig 10 ima-sig /tmp/metadata If parsing of RPM headers is done by the kernel, the measurement list will look like: 10 ima-ng boot_aggregate 10 ima-ng /etc/ima/digest_lists/metadata A built-in policy should enable appraisal of tmpfs. If not, patch 11/15 disables digest lookup for appraisal. Since generic digest lists will have a security.ima extended attribute (they are mutable files), appraisal verification will succeed. With this solution, digital signatures cannot be required, because generic digest lists will have a HMAC. For appraisal, it becomes necessary to ensure that only digest lists written by the parser can be processed by IMA. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/7/2017 7:06 PM, Matthew Garrett wrote: On Tue, Nov 7, 2017 at 12:53 PM, Roberto Sassu <roberto.sa...@huawei.com> wrote: On 11/7/2017 3:49 PM, Matthew Garrett wrote: RPM's hardly universal, and distributions are in the process of moving away from using it for distributing non-core applications (Flatpak and Snap are becoming increasingly popular here). I think this needs to be a generic solution rather than having the kernel tied to a specific package format. Support for new digest list formats can be easily added. Digest list metadata includes the digest list type, so that the appropriate parser is selected. But we're still left in a state where the kernel has to end up supporting a number of very niche formats, and userland agility is tied to the kernel. I think it makes significantly more sense to push the problem out to userland. At least for appraisal, digest lists must be parsed by the kernel. If the parser is moved to userspace, I don't know if we are able to provide the same guarantee, that the correct set of digests has been uploaded to IMA. A new measurement can be added, when IMA receives the digests, but a verifier has to verify the signature of the original file, perform format conversion, calculate the digest and compare it with that in the new IMA measurement. If digest lists are parsed directly by the kernel, then the signature can be verified directly. Digest lists should be parsed directly by the kernel, because processing the lists in userspace would increase the chances that a compromised tool does not upload to the kernel the expected digests. Also, digest lists must be processed before init, otherwise appraisal will deny the execution. Lastly, the mechanism of parsing files from the kernel is already used to parse the IMA policy. Isn't failing to upload the expected digest list just a DoS? We already expect to load keys from initramfs, so it seems fine to parse stuff there - what's the problem with extracting information from RPMs, translating them to the generic format and pushing that into the kernel? The main problem is that the digest list measurement, performed when the parser accesses the file containing the RPM header, might not reflect what IMA uses for digest lookup. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/7/2017 3:49 PM, Matthew Garrett wrote: On Tue, Nov 7, 2017 at 2:36 AM, Roberto Sassu <roberto.sa...@huawei.com> wrote: Finally, digest lists address also the third issue because Linux distribution vendors already provide the digests of files included in each RPM package. The digest list is stored in the RPM header, signed by the vendor. RPM's hardly universal, and distributions are in the process of moving away from using it for distributing non-core applications (Flatpak and Snap are becoming increasingly popular here). I think this needs to be a generic solution rather than having the kernel tied to a specific package format. Support for new digest list formats can be easily added. Digest list metadata includes the digest list type, so that the appropriate parser is selected. I defined a new generic format for digest lists in Patch 7/15. I would appreciate if we can discuss this format, and if you can give me suggestions about how to improve it. I think it would not be a problem to support your use case and associate metadata to each digest. Digest lists should be parsed directly by the kernel, because processing the lists in userspace would increase the chances that a compromised tool does not upload to the kernel the expected digests. Also, digest lists must be processed before init, otherwise appraisal will deny the execution. Lastly, the mechanism of parsing files from the kernel is already used to parse the IMA policy. Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 00/15] ima: digest list feature
On 11/7/2017 2:37 PM, Mimi Zohar wrote: Hi Roberto, On Tue, 2017-11-07 at 11:36 +0100, Roberto Sassu wrote: IMA is a security module with the objective of reporting or enforcing the integrity of a system, by measuring files accessed with the execve(), mmap() and open() system calls. For reporting, it takes advantage of the TPM and extends a PCR with the digest of an evaluated event. For enforcing, it returns a value which is zero if the operation should be allowed, negative if it should be denied. Measuring files of an operating system introduces three main issues. First, since the overhead introduced by the TPM is noticeable, the performance of the system decreases linearly with the number of measurements taken. This can be seen especially at boot time. I've said this previously. The solution IS FIRST to improve the performance of the TPM device driver, before finding solutions around it. TPM performance patches: a233a0289cf9 "tpm: msleep() delays - replace with usleep_range() in i2c nuvoton driver" 0afb7118ae02 "tpm: add sleep only for retry in i2c_nuvoton_write_status()" 9f3fc7bcddcb "tpm: replace msleep() with usleep_range() in TPM 1.2/2.0 generic drivers" Nayna Jain submitted additional performance improvements, that were posted https://www.spinics.net/lists/linux-integrity/msg00238.html and are currently being tested. Even after these TPM performance improvements, there are still more TPM performance improvements. Hi Mimi I applied the patches you mentioned, and indeed the performance improvement is great, from 1 minute and 30 seconds to 24 seconds compared to the normal boot time of 8.5 seconds. However, especially for embedded devices performances requirements might be more strict, and much more files might be measured. For desktops, also it would be desirable to have low latency. Second, managing large measurement lists requires computation power and network bandwidth. "Large" for whom? Large for the attestation server? Large for the client? Smaller devices would have fewer measurements than larger devices. We're not discussing gigabytes/terabytes of data here. Attestation servers should be able to handle the bandwidth. If it becomes a problem, then the attestation server/client communication could be optimized to send just the recent measurements, not the entire measurement list. I'm not considering an individual system, but a datacenter with several nodes. An attestation server processing for example 200 measurement lists with 5000 entries must be much more powerful than one that processes the same number of lists with 10 entries. Third, it is necessary to obtain reference measurements (i.e. digests of software known to be good) to evaluate/enforce the integrity of the system. If file signatures are used to enforce access, Linux distribution vendors have to modify their building systems in order to include signatures in their packages. Although IMA-appraisal verifies file integrity based on either a file hash or signature, they are not equivalent. File signatures provide file provenance. Not only does the file hash have to match, but a certificate used to sign the file data must be loaded onto the IMA keyring. File hashes should be limited to mutable files. Digest lists work the same. If appraisal is in enforcing mode, digest lists must have a valid digital signature. Instead of working around the problem of a lack of file signatures in software packages, help promote including them so that there are measurement and signature chains of trust anchored in hardware. One of the key point of digest lists feature is that it reuses information that is already available, while providing the same security properties. I find it difficult to promote a solution that would introduce redundant information and complicate the management of Linux distributions. Digest lists aim at mitigating these issues. A digest list is a list of digests that are taken by IMA as reference measurements and loaded before files are accessed. Then, IMA compares calculated digests of accessed files with digests from loaded digest lists. If the digest is found, measurement, appraisal and audit are not performed. Although the previous patch set did not break userspace per-se, it changed the existing meaning of the IMA measurement list. Without taking into account my previous comments, this patch set makes similar changes to IMA-appraisal and IMA-measurement. For appraisal, I think only the verification method changes: instead of verifying file signatures individually, IMA verifies one signature per many file digests. Instead of including the individual file measurements, the previous version of this patch set, I assume it hasn't changed, includes a hash of a file containing a list of all potential file measurements, not the actual file measurements. Digest lists do not introduce false positives due to not including a measurement. Fi
[PATCH v2 02/15] ima: generalize ima_write_policy()
This patch renames ima_write_policy() to ima_write_data(). Also, it determines the kernel_read_file_id from the dentry associated to the file, and passes it to ima_read_file(). Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 55 ++--- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 27de4558303e..864d34581081 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -28,6 +28,21 @@ static DEFINE_MUTEX(ima_write_mutex); +static struct dentry *ima_dir; +static struct dentry *binary_runtime_measurements; +static struct dentry *ascii_runtime_measurements; +static struct dentry *runtime_measurements_count; +static struct dentry *violations; +static struct dentry *ima_policy; + +static enum kernel_read_file_id ima_get_file_id(struct dentry *dentry) +{ + if (dentry == ima_policy) + return READING_POLICY; + + return READING_UNKNOWN; +} + bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) { @@ -315,11 +330,12 @@ static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) return pathlen; } -static ssize_t ima_write_policy(struct file *file, const char __user *buf, - size_t datalen, loff_t *ppos) +static ssize_t ima_write_data(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) { char *data; ssize_t result; + enum kernel_read_file_id file_id = ima_get_file_id(file->f_path.dentry); if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -340,34 +356,33 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free; if (data[0] == '/') { - result = ima_read_file(data, READING_POLICY); - } else if (ima_appraise & IMA_APPRAISE_POLICY) { - pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); - integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, - "policy_update", "signed policy required", - 1, 0); - if (ima_appraise & IMA_APPRAISE_ENFORCE) - result = -EACCES; + result = ima_read_file(data, file_id); + } else if (file_id == READING_POLICY) { + if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("IMA: signed policy file (specified " + "as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", "signed policy required", + 1, 0); + if (ima_appraise & IMA_APPRAISE_ENFORCE) + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } } else { - result = ima_parse_add_rule(data); + pr_err("Unknown data type\n"); + result = -EINVAL; } mutex_unlock(_write_mutex); out_free: kfree(data); out: - if (result < 0) + if (file_id == READING_POLICY && result < 0) valid_policy = 0; return result; } -static struct dentry *ima_dir; -static struct dentry *binary_runtime_measurements; -static struct dentry *ascii_runtime_measurements; -static struct dentry *runtime_measurements_count; -static struct dentry *violations; -static struct dentry *ima_policy; - enum ima_fs_flags { IMA_FS_BUSY, }; @@ -446,7 +461,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, - .write = ima_write_policy, + .write = ima_write_data, .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek, -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 07/15] ima: add parser of compact digest list
This patch introduces the parser for the compact digest list. Its format is: entry_id[2] count[4] data_len[4] data[data_len] entry_id[2] count[4] data_len[4] data[data_len] ... entry_id, count and data_len are in little endian. This format is suitable to store a large number of digests, as there is no metadata provided for each. Digests (which have all the same size) are concatenated together and placed after the header. COMPACT_DIGEST (0) and COMPACT_DIGEST_MUTABLE (1) entry IDs are supported. If the entry ID is COMPACT_DIGEST and appraisal is in enforcing mode, file updates are denied. If the entry ID is COMPACT_DIGEST_MUTABLE, file updates are permitted. Changelog v1: - Renamed COMPACT_LIST_ID_DIGEST to COMPACT_DIGEST - Added support for immutable/mutable files Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_digest_list.c | 66 1 file changed, 66 insertions(+) diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 28172424e5a2..6ad00ba32c94 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -23,6 +23,69 @@ enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, DATA__LAST}; +enum digest_data_types {DATA_TYPE_COMPACT_LIST}; + +enum compact_list_entry_ids {COMPACT_DIGEST, COMPACT_DIGEST_MUTABLE}; + +struct compact_list_hdr { + u16 entry_id; + u32 count; + u32 datalen; +} __packed; + +static int ima_parse_compact_list(loff_t size, void *buf) +{ + void *bufp = buf, *bufendp = buf + size; + int digest_len = hash_digest_size[ima_hash_algo]; + struct compact_list_hdr *hdr; + u8 is_mutable = 0; + int ret, i; + + while (bufp < bufendp) { + if (bufp + sizeof(*hdr) > bufendp) { + pr_err("compact list, missing header\n"); + return -EINVAL; + } + + hdr = bufp; + + if (ima_canonical_fmt) { + hdr->entry_id = le16_to_cpu(hdr->entry_id); + hdr->count = le32_to_cpu(hdr->count); + hdr->datalen = le32_to_cpu(hdr->datalen); + } + + switch (hdr->entry_id) { + case COMPACT_DIGEST_MUTABLE: + is_mutable = 1; + case COMPACT_DIGEST: + break; + default: + pr_err("compact list, invalid data type\n"); + return -EINVAL; + } + + bufp += sizeof(*hdr); + + for (i = 0; i < hdr->count && +bufp + digest_len <= bufendp; i++) { + ret = ima_add_digest_data_entry(bufp, is_mutable); + if (ret < 0 && ret != -EEXIST) + return ret; + + bufp += digest_len; + } + + if (i != hdr->count || + bufp != (void *)hdr + sizeof(*hdr) + hdr->datalen) { + pr_err("compact list, invalid data\n"); + return -EINVAL; + } + } + + return 0; +} + static int ima_parse_digest_list_data(struct ima_field_data *data) { void *digest_list; @@ -47,6 +110,9 @@ static int ima_parse_digest_list_data(struct ima_field_data *data) } switch (data_type) { + case DATA_TYPE_COMPACT_LIST: + ret = ima_parse_compact_list(digest_list_size, digest_list); + break; default: pr_err("Parser for data type %d not implemented\n", data_type); ret = -EINVAL; -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 06/15] ima: add parser of digest lists metadata
Digest lists can be uploaded to IMA by supplying the path of their metadata. Digest list metadata are: - DATA_ALGO: algorithm of the digests to be uploaded - DATA_DIGEST: digest of the file containing the digest list - DATA_SIGNATURE: signature of the file containing the digest list - DATA_FILE_PATH: pathname - DATA_REF_ID: reference ID of the digest list - DATA_TYPE: type of digest list The new function ima_load_digest_list_metadata() loads digest list metadata from a predefined position (/etc/ima/digest_lists/metadata), when rootfs becomes available. Digest lists must be loaded before IMA appraisal is in enforcing mode. The new function ima_parse_digest_list_metadata() parses the metadata and loads each digest list individually. Then, it parses the data according to the data type specified. To avoid the delay due to extending a PCR for each digest list, digests of digest lists are added to the hash table. If appraisal is in enforcing mode, this is done only if the signature verification succeeds. IMA does not add the digest of an accessed file to the measurement list if the digest is found in the hash table. Changelog v1: - Verify signature of digest lists if appraisal is enabled - Load digest lists when rootfs is available - Ignore digest lists if no policy is loaded Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- include/linux/fs.h | 2 + security/integrity/iint.c| 1 + security/integrity/ima/Kconfig | 19 security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 12 +++ security/integrity/ima/ima_digest_list.c | 152 +++ security/integrity/integrity.h | 8 ++ 7 files changed, 195 insertions(+) create mode 100644 security/integrity/ima/ima_digest_list.c diff --git a/include/linux/fs.h b/include/linux/fs.h index 8d7d2850963c..06737235665b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2793,6 +2793,8 @@ extern int do_pipe_flags(int *, int); id(KEXEC_INITRAMFS, kexec-initramfs)\ id(POLICY, security-policy) \ id(X509_CERTIFICATE, x509-certificate) \ + id(DIGEST_LIST_METADATA, digest-list-metadata) \ + id(DIGEST_LIST, digest-list)\ id(MAX_ID, ) #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c84e05866052..68c14d1dfc0c 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -209,4 +209,5 @@ void __init integrity_load_keys(void) { ima_load_x509(); evm_load_x509(); + ima_load_digest_list_metadata(); } diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 35ef69312811..fa40cee1e1a2 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -227,3 +227,22 @@ config IMA_APPRAISE_SIGNED_INIT default n help This option requires user-space init to be signed. + +config IMA_DIGEST_LIST + bool "Measure/appraise/audit files depending on uploaded digest lists" + depends on IMA + default n + help + This option allows users to load digest lists. If a measured file + has the same digest of one from loaded lists, IMA will not create + a new measurement entry or an audit log. They will be created only + when digest lists are loaded. If appraisal is enabled, access will + be permitted if the digest is in the digest list. File updates + will be permitted if, in addition, the digest is marked as mutable. + +config IMA_DIGEST_LIST_METADATA_PATH + string "IMA digest list metadata path" + depends on IMA_DIGEST_LIST + default "/etc/ima/digest_lists/metadata" + help + This option defines IMA digest list metadata path. diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 29f198bde02b..00dbe3a8cb71 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -9,4 +9,5 @@ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o +ima-$(CONFIG_IMA_DIGEST_LIST) += ima_digest_list.o obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 1f6591a57fea..1f43284788eb 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -158,6 +158,18 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); struct ima_digest *ima_lookup_loaded_digest(u8 *digest); int ima_add_digest_data_entry(u8 *digest, u8 is_mutable); +#ifdef CONFIG_IMA_DIGEST_LIST +void __init ima_load_
[PATCH v2 09/15] ima: introduce securityfs interfaces for digest lists
This patch introduces the file 'digest_lists' in the securityfs filesystem, to load digest lists metadata. IMA will parse the metadata and load the digest lists from the path provided. It also introduces 'digests_count', to show the number of digests stored in the ima_digests_htable hash table. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> Changelog v1: - Deny upload of digest lists if no policy is loaded --- security/integrity/ima/ima_fs.c | 26 +- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 4158ced5d3c9..1ed717d94487 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -34,11 +34,15 @@ static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; static struct dentry *ima_policy; +static struct dentry *digest_lists; +static struct dentry *digests_count; static enum kernel_read_file_id ima_get_file_id(struct dentry *dentry) { if (dentry == ima_policy) return READING_POLICY; + else if (dentry == digest_lists) + return READING_DIGEST_LIST_METADATA; return READING_UNKNOWN; } @@ -66,6 +70,8 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, val = _htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) val = _htable.len; + else if (filp->f_path.dentry == digests_count) + val = _digests_htable.len; len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); @@ -301,6 +307,9 @@ static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) pr_debug("rule: %s\n", p); rc = ima_parse_add_rule(p); + } else if (file_id == READING_DIGEST_LIST_METADATA) { + rc = ima_parse_digest_list_metadata(size, datap); + datap += rc; } if (rc < 0) break; @@ -401,7 +410,8 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) read_allowed = true; seq_ops = _policy_seqops; #endif - } + } else if (file_id == READING_DIGEST_LIST_METADATA && !ima_policy_flag) + return -EACCES; if (!(filp->f_flags & O_WRONLY)) { if (!read_allowed) @@ -510,8 +520,22 @@ int __init ima_fs_init(void) if (IS_ERR(ima_policy)) goto out; +#ifdef CONFIG_IMA_DIGEST_LIST + digest_lists = securityfs_create_file("digest_lists", S_IWUSR, ima_dir, + NULL, _data_upload_ops); + if (IS_ERR(digest_lists)) + goto out; + + digests_count = securityfs_create_file("digests_count", + S_IRUSR | S_IRGRP, ima_dir, + NULL, _htable_value_ops); + if (IS_ERR(digests_count)) + goto out; +#endif return 0; out: + securityfs_remove(digests_count); + securityfs_remove(digest_lists); securityfs_remove(violations); securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements); -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 10/15] ima: disable digest lookup if digest lists are not checked
This patch introduces two new hooks DIGEST_LIST_METADATA_CHECK and DIGEST_LIST_CHECK, which are called respectively when parsing digest list metadata and digest lists. It also checks that rules for these two hooks are always specified in the current policy. Without them, digest lists could be uploaded to IMA without adding a new entry to the measurement list, without verifying the signature, or without auditing the operation. Digest lookup is disabled for each missing policy action (measure, appraise, audit). Digest lookup is also disabled if CONFIG_IMA_DIGEST_LIST is not defined. Changelog v1: - clear IMA_MEASURE action flag only if it was in the policy - check if at least one action is allowed before searching the file digest - retrieve ima_digest structure - set IMA action flags in ima_disable_digest_lookup - added DIGEST_LIST_METADATA_CHECK hook - update MEASURE/APPRAISE policies Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima.h| 2 ++ security/integrity/ima/ima_main.c | 38 +++-- security/integrity/ima/ima_policy.c | 16 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 1f43284788eb..4b3b1ca5c09a 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -204,6 +204,8 @@ static inline unsigned long ima_hash_key(u8 *digest) hook(KEXEC_KERNEL_CHECK)\ hook(KEXEC_INITRAMFS_CHECK) \ hook(POLICY_CHECK) \ + hook(DIGEST_LIST_METADATA_CHECK)\ + hook(DIGEST_LIST_CHECK) \ hook(MAX_CHECK) #define __ima_hook_enumify(ENUM) ENUM, diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 766fe2e77419..840362734f91 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -29,6 +29,12 @@ int ima_initialized; +#ifdef CONFIG_IMA_DIGEST_LIST +static int ima_disable_digest_lookup; +#else +static int ima_disable_digest_lookup = IMA_DO_MASK & ~IMA_APPRAISE_SUBMASK; +#endif + #ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; #else @@ -168,12 +174,20 @@ static int process_measurement(struct file *file, char *buf, loff_t size, char *pathbuf = NULL; char filename[NAME_MAX]; const char *pathname = NULL; - int rc = -ENOMEM, action, must_appraise; + int rc = -ENOMEM, action, action_done, must_appraise, digest_lookup; int pcr = CONFIG_IMA_MEASURE_PCR_IDX; + struct ima_digest *found_digest = NULL; struct evm_ima_xattr_data *xattr_value = NULL; int xattr_len = 0; bool violation_check; enum hash_algo hash_algo; + int disable_mask = (func == DIGEST_LIST_CHECK) ? + IMA_DO_MASK & ~IMA_APPRAISE_SUBMASK : + IMA_DO_MASK & ~(IMA_APPRAISE | IMA_APPRAISE_SUBMASK); + + if ((func == DIGEST_LIST_METADATA_CHECK || func == DIGEST_LIST_CHECK) && + !ima_policy_flag) + ima_disable_digest_lookup = disable_mask; if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; @@ -185,6 +199,9 @@ static int process_measurement(struct file *file, char *buf, loff_t size, action = ima_get_action(inode, mask, func, ); violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); + if (func == DIGEST_LIST_METADATA_CHECK || func == DIGEST_LIST_CHECK) + ima_disable_digest_lookup |= (~action & disable_mask); + if (!action && !violation_check) return 0; @@ -242,6 +259,21 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if (rc != 0 && rc != -EBADF && rc != -EINVAL) goto out_digsig; + digest_lookup = action & ~ima_disable_digest_lookup; + if (digest_lookup) { + found_digest = ima_lookup_loaded_digest(iint->ima_hash->digest); + if (found_digest) { + action_done = digest_lookup & (IMA_MEASURE | IMA_AUDIT); + action &= ~action_done; + iint->flags |= (action_done << 1); + + if (!(digest_lookup & IMA_APPRAISE)) + found_digest = NULL; + if (digest_lookup & IMA_MEASURE) + iint->measured_pcrs |= (0x1 << pcr); + } + } + if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(>f_path, , filename); @@ -378,7 +410,9 @@ static int read_idmap[READING_MAX_ID] = { [READING_MODULE] = MODULE_CHECK, [READING_K
[PATCH v2 15/15] ima: add Documentation/security/IMA-digest-lists.txt
This patch adds the documentation of digest lists. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- Documentation/security/IMA-digest-lists.txt | 161 1 file changed, 161 insertions(+) create mode 100644 Documentation/security/IMA-digest-lists.txt diff --git a/Documentation/security/IMA-digest-lists.txt b/Documentation/security/IMA-digest-lists.txt new file mode 100644 index ..afa860bbe53e --- /dev/null +++ b/Documentation/security/IMA-digest-lists.txt @@ -0,0 +1,161 @@ + +Digest Lists + + + +INTRODUCTION + + +IMA is a security module with the objective of reporting or enforcing the +integrity of a system, by measuring files accessed with the execve(), +mmap() and open() system calls. For reporting, it takes advantage of the +TPM and extends a PCR with the digest of an evaluated event. For enforcing, +it returns a value which is zero if the operation should be allowed, +negative if it should be denied. + +Measuring files of an operating system introduces three main issues. First, +since the overhead introduced by the TPM is noticeable, the performance of +the system decreases linearly with the number of measurements taken. This +can be seen especially at boot time. Second, managing large measurement +lists requires computation power and network bandwidth. Third, it is +necessary to obtain reference measurements (i.e. digests of software known +to be good) to evaluate/enforce the integrity of the system. If file +signatures are used to enforce access, Linux distribution vendors have to +modify their building systems in order to include signatures in their +packages. + +Digest lists aim at mitigating these issues. A digest list is a list of +digests that are taken by IMA as reference measurements and loaded before +files are accessed. Then, IMA compares calculated digests of accessed files +with digests from loaded digest lists. If the digest is found, measurement, +appraisal and audit are not performed. + +Multiple digest lists can be loaded at the same time, by providing to IMA +metadata for each list: digest, signature and path. The digest is specified +so that loaded digest lists can be identified only with the measurement of +metadata. The signature is used for appraisal. If the verification +succeeds, IMA loads the digest list even if security.ima is missing. + +Digest lists address the first issue because the TPM is used only if the +digest of a measured file is unknown. On a minimal system, 10 of 1400 +measurements are unknown because of mutable files (e.g. log files). + +Digest lists mitigate the second issue because, since digest lists do not +change, they don't have to be sent at every remote attestation. Sending +unknown measurements and a reference to digest lists would be sufficient. + +Finally, digest lists address also the third issue because Linux +distribution vendors already provide the digests of files included in each +RPM package. The digest list is stored in the RPM header, signed by the +vendor. + +When using digest lists, a limitation must be considered. Since a +measurement is not reported if the digest of an accessed file is found in a +digest list, the measurement list does not show which files have been +actually accessed, and in which sequence. + +A possible solution would be to load a list with digest of files which are +usually accessed. Also, it is possible to selectively enable digest list +lookup only for a subset of IMA policy rules. For example, a policy could +enable digest lookup only for file accesses from the TCB and disable it +for execve() and mmap() from regular users. + + + +SETUP += + +Digest lists should be placed in the /etc/ima/digest_lists directory and +metadata should be written to /etc/ima/digest_lists/metadata. + +If digest lists are included in the initial ram disk, IMA will load them +early in the boot process. Otherwise, a patched systemd can check if the +file with digest list metadata exists in the filesystem and, if yes, send +the path to IMA through the 'digest_lists' securityfs interface. The main +use case for the patched systemd is to load digest lists of newly installed +packages, which are not included in the initial ram disk. + + + +FORMATS +=== + +The format of digest list metadata is: + +algo[2] +digest_len[4] digest[digest_len] +signature_len[4] signature[signature_len] +path_len[4] path[path_len] +ref_id_len[4] ref_id[ref_id_len] +list_type[2] + +algo and list_type are in little endian. + +algo values are defined in include/uapi/linux/hash_info.h. The algorithms +in the list metadata must be the same of ima_hash_algo (algorithm used by +IMA to calculate the file digest). + +list type values: + +0: compact digest list +1: RPM package header + + +The format of the compact digest list is: + +entry_id[2] count[4] data_len[4] +data[data_len] +[...] +entry_id[2] count[4] data_len[4] +data[data_len] + +entry_id, count and data_len are in
[PATCH v2 14/15] ima: add support for appraisal with digest lists
Appraisal verification consists on comparing the calculated digest of an accessed file with the value of the security.ima extended attribute. With digest lists, appraisal verification succeeds if the calculated digest is included in a list. Since the digital signature of each digest list is verified, it is not possible to allow access of unauthorized files. For mutable files, IMA writes the current digest to security.ima so that next file accesses are allowed even if the files have been modified. For immutable files, IMA writes security.ima only if also additional extended attributes should be protected by EVM. Otherwise, security.ima would be redundant, as digest lists provide reference values. When IMA writes security.ima, EVM calculates the HMAC based on the current value of protected extended attributes. Without file signatures, initial extended attribute values will not checked until digest lists include them. When extended attribute values are available, IMA will check them as the same as the digest, and will not write security.ima for immutable files if values are provided for all extended attributes protected by EVM. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima.h | 6 +++-- security/integrity/ima/ima_appraise.c | 47 +++ security/integrity/ima/ima_main.c | 6 +++-- security/integrity/integrity.h| 2 ++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index ddd0e1e7e99b..5f8e0740a33e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -261,7 +261,8 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, -int xattr_len, int opened); +int xattr_len, int opened, +struct ima_digest *found_digest); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, @@ -277,7 +278,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len, int opened) + int xattr_len, int opened, + struct ima_digest *found_digest) { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 1b2236e637ff..fd03a0278fba 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -201,17 +201,27 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, -int xattr_len, int opened) +int xattr_len, int opened, +struct ima_digest *found_digest) { static const char op[] = "appraise_data"; char *cause = "unknown"; struct dentry *dentry = file_dentry(file); struct inode *inode = d_backing_inode(dentry); enum integrity_status status = INTEGRITY_UNKNOWN; - int rc = xattr_len, hash_start = 0; + struct evm_ima_xattr_data digest_list_value; + char *list_metadata = XATTR_NAME_IMA; + int rc = xattr_len, hash_start = 0, cache_flags_disabled = 0; if (!(inode->i_opflags & IOP_XATTR)) - return INTEGRITY_UNKNOWN; + return found_digest ? INTEGRITY_PASS : INTEGRITY_UNKNOWN; + + if (found_digest && (!rc || rc == -ENODATA)) { + digest_list_value.type = found_digest->is_mutable ? + IMA_DIGEST_LIST_MUTABLE : IMA_DIGEST_LIST_IMMUTABLE; + xattr_value = _list_value; + rc = sizeof(*xattr_value); + } if (rc <= 0) { if (rc && rc != -ENODATA) @@ -228,6 +238,9 @@ int ima_appraise_measurement(enum ima_hooks func, goto out; } + if (xattr_value == _list_value) + goto no_evm_check; + status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
[PATCH v2 08/15] ima: add parser of RPM package headers
This patch introduces a parser of RPM package headers. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distribution vendors. RPM header signatures can be provided as digest list metadata. The parser also checks the RPMTAG_FILEMODES section. If the file is not executable, the setuid/setgid/sticky bits are not set and has write permission, the digest is marked as mutable (file updates are permitted if appraisal is in enforcing mode). Changelog v1: - Moved parser of file digests outside the first loop - Added support for immutable/mutable files Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_digest_list.c | 110 ++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 6ad00ba32c94..664a4994efbb 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -19,11 +19,14 @@ #include "ima.h" #include "ima_template_lib.h" +#define RPMTAG_FILEDIGESTS 1035 +#define RPMTAG_FILEMODES 1030 + enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, DATA__LAST}; -enum digest_data_types {DATA_TYPE_COMPACT_LIST}; +enum digest_data_types {DATA_TYPE_COMPACT_LIST, DATA_TYPE_RPM}; enum compact_list_entry_ids {COMPACT_DIGEST, COMPACT_DIGEST_MUTABLE}; @@ -33,6 +36,20 @@ struct compact_list_hdr { u32 datalen; } __packed; +struct rpm_hdr { + u32 magic; + u32 reserved; + u32 tags; + u32 datasize; +} __packed; + +struct rpm_entryinfo { + int32_t tag; + u32 type; + int32_t offset; + u32 count; +} __packed; + static int ima_parse_compact_list(loff_t size, void *buf) { void *bufp = buf, *bufendp = buf + size; @@ -86,6 +103,94 @@ static int ima_parse_compact_list(loff_t size, void *buf) return 0; } +static int ima_parse_rpm(loff_t size, void *buf) +{ + void *bufp = buf, *bufendp = buf + size; + struct rpm_hdr *hdr = bufp; + u32 tags = be32_to_cpu(hdr->tags); + struct rpm_entryinfo *entry; + void *datap = bufp + sizeof(*hdr) + tags * sizeof(struct rpm_entryinfo); + void *digests = NULL, *modes = NULL; + u32 digests_count, modes_count; + int digest_len = hash_digest_size[ima_hash_algo]; + u8 digest[digest_len]; + int ret, i; + + const unsigned char rpm_header_magic[8] = { + 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + + if (size < sizeof(*hdr)) { + pr_err("Missing RPM header\n"); + return -EINVAL; + } + + if (memcmp(bufp, rpm_header_magic, sizeof(rpm_header_magic))) { + pr_err("Invalid RPM header\n"); + return -EINVAL; + } + + bufp += sizeof(*hdr); + + for (i = 0; i < tags && (bufp + sizeof(*entry)) <= bufendp; +i++, bufp += sizeof(*entry)) { + entry = bufp; + + if (be32_to_cpu(entry->tag) == RPMTAG_FILEDIGESTS) { + digests = datap + be32_to_cpu(entry->offset); + digests_count = be32_to_cpu(entry->count); + } + if (be32_to_cpu(entry->tag) == RPMTAG_FILEMODES) { + modes = datap + be32_to_cpu(entry->offset); + modes_count = be32_to_cpu(entry->count); + } + if (digests && modes) + break; + } + + if (digests == NULL) + return 0; + + for (i = 0; i < digests_count && digests < bufendp; i++) { + u8 is_mutable = 0; + u16 mode; + + if (strlen(digests) == 0) { + digests++; + continue; + } + + if (modes) { + if (modes + (i + 1) * sizeof(mode) > bufendp) { + pr_err("RPM header read at invalid offset\n"); + return -EINVAL; + } + + mode = be16_to_cpu(*(u16 *)(modes + i * sizeof(mode))); + if (!(mode & (S_IXUGO | S_ISUID | S_ISGID | S_ISVTX)) && + (mode & S_IWUGO)) + is_mutable = 1; + } + + if (digests + digest_len * 2 + 1 > bufendp) { + pr_err("RPM header read at invalid offset\n");
[PATCH v2 05/15] ima: add functions to manage digest lists
This patch first introduces a new structure called ima_digest, which contains a digest parsed from a digest list. It has been preferred to ima_queue_entry, as the existing structure includes an additional member (a list head), which is not necessary for digest lookup. It also introduces the is_mutable field, which indicates if a file with a given digest can be updated or not. Finally, this patch introduces functions to lookup and add a digest to the new ima_digests_htable hash table. Changelog v1: - added support for immutable/mutable files Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima.h | 9 security/integrity/ima/ima_queue.c | 42 ++ 2 files changed, 51 insertions(+) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d52b487ad259..1f6591a57fea 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -107,6 +107,12 @@ struct ima_queue_entry { }; extern struct list_head ima_measurements; /* list of all measurements */ +struct ima_digest { + struct hlist_node hnext; + u8 is_mutable; + u8 digest[0]; +}; + /* Some details preceding the binary serialized measurement list */ struct ima_kexec_hdr { u16 version; @@ -150,6 +156,8 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); struct ima_template_desc *ima_template_desc_current(void); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); +struct ima_digest *ima_lookup_loaded_digest(u8 *digest); +int ima_add_digest_data_entry(u8 *digest, u8 is_mutable); int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); @@ -166,6 +174,7 @@ struct ima_h_table { struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; }; extern struct ima_h_table ima_htable; +extern struct ima_h_table ima_digests_htable; static inline unsigned long ima_hash_key(u8 *digest) { diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index a02a86d51102..96c91c413430 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -42,6 +42,11 @@ struct ima_h_table ima_htable = { .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT }; +struct ima_h_table ima_digests_htable = { + .len = ATOMIC_LONG_INIT(0), + .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT +}; + /* mutex protects atomicity of extending measurement list * and extending the TPM PCR aggregate. Since tpm_extend can take * long (and the tpm driver uses a mutex), we can't use the spinlock. @@ -212,3 +217,40 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry) mutex_unlock(_extend_list_mutex); return result; } + +struct ima_digest *ima_lookup_loaded_digest(u8 *digest) +{ + struct ima_digest *d = NULL; + int digest_len = hash_digest_size[ima_hash_algo]; + unsigned int key = ima_hash_key(digest); + + rcu_read_lock(); + hlist_for_each_entry_rcu(d, _digests_htable.queue[key], hnext) { + if (memcmp(d->digest, digest, digest_len) == 0) + break; + } + rcu_read_unlock(); + return d; +} + +int ima_add_digest_data_entry(u8 *digest, u8 is_mutable) +{ + struct ima_digest *d = ima_lookup_loaded_digest(digest); + int digest_len = hash_digest_size[ima_hash_algo]; + unsigned int key = ima_hash_key(digest); + + if (d) { + d->is_mutable = is_mutable; + return -EEXIST; + } + + d = kmalloc(sizeof(*d) + digest_len, GFP_KERNEL); + if (d == NULL) + return -ENOMEM; + + d->is_mutable = is_mutable; + memcpy(d->digest, digest, digest_len); + hlist_add_head_rcu(>hnext, _digests_htable.queue[key]); + atomic_long_inc(_digests_htable.len); + return 0; +} -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 04/15] ima: use ima_show_htable_value to show hash table data
This patch removes ima_show_htable_violations() and ima_show_measurements_count(). ima_show_htable_value(), called by those functions, determines which hash table data should be copied to the buffer depending on the dentry of the file passed as argument. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 38 -- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index a5b82e075ec8..4158ced5d3c9 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -55,38 +55,24 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup); static int valid_policy = 1; #define TMPBUFLEN 12 -static ssize_t ima_show_htable_value(char __user *buf, size_t count, -loff_t *ppos, atomic_long_t *val) +static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, +size_t count, loff_t *ppos) { + atomic_long_t *val = NULL; char tmpbuf[TMPBUFLEN]; ssize_t len; + if (filp->f_path.dentry == violations) + val = _htable.violations; + else if (filp->f_path.dentry == runtime_measurements_count) + val = _htable.len; + len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); } -static ssize_t ima_show_htable_violations(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, _htable.violations); -} - -static const struct file_operations ima_htable_violations_ops = { - .read = ima_show_htable_violations, - .llseek = generic_file_llseek, -}; - -static ssize_t ima_show_measurements_count(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, _htable.len); - -} - -static const struct file_operations ima_measurements_count_ops = { - .read = ima_show_measurements_count, +static const struct file_operations ima_htable_value_ops = { + .read = ima_show_htable_value, .llseek = generic_file_llseek, }; @@ -508,13 +494,13 @@ int __init ima_fs_init(void) runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, - _measurements_count_ops); + _htable_value_ops); if (IS_ERR(runtime_measurements_count)) goto out; violations = securityfs_create_file("violations", S_IRUSR | S_IRGRP, - ima_dir, NULL, _htable_violations_ops); + ima_dir, NULL, _htable_value_ops); if (IS_ERR(violations)) goto out; -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 01/15] ima: generalize ima_read_policy()
Rename ima_read_policy() to ima_read_file(), and add file_id as new parameter. If file_id is equal to READING_POLICY, ima_read_file() behavior remains unchanged. ima_read_file() will be used to read digest list metadata. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 18 -- 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index fa540c0469da..27de4558303e 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -272,7 +272,7 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; -static ssize_t ima_read_policy(char *path) +static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) { void *data; char *datap; @@ -285,16 +285,22 @@ static ssize_t ima_read_policy(char *path) datap = path; strsep(, "\n"); - rc = kernel_read_file_from_path(path, , , 0, READING_POLICY); + rc = kernel_read_file_from_path(path, , , 0, file_id); if (rc < 0) { pr_err("Unable to open file: %s (%d)", path, rc); return rc; } datap = data; - while (size > 0 && (p = strsep(, "\n"))) { - pr_debug("rule: %s\n", p); - rc = ima_parse_add_rule(p); + while (size > 0) { + if (file_id == READING_POLICY) { + p = strsep(, "\n"); + if (p == NULL) + break; + + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + } if (rc < 0) break; size -= rc; @@ -334,7 +340,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free; if (data[0] == '/') { - result = ima_read_policy(data); + result = ima_read_file(data, READING_POLICY); } else if (ima_appraise & IMA_APPRAISE_POLICY) { pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 00/15] ima: digest list feature
IMA is a security module with the objective of reporting or enforcing the integrity of a system, by measuring files accessed with the execve(), mmap() and open() system calls. For reporting, it takes advantage of the TPM and extends a PCR with the digest of an evaluated event. For enforcing, it returns a value which is zero if the operation should be allowed, negative if it should be denied. Measuring files of an operating system introduces three main issues. First, since the overhead introduced by the TPM is noticeable, the performance of the system decreases linearly with the number of measurements taken. This can be seen especially at boot time. Second, managing large measurement lists requires computation power and network bandwidth. Third, it is necessary to obtain reference measurements (i.e. digests of software known to be good) to evaluate/enforce the integrity of the system. If file signatures are used to enforce access, Linux distribution vendors have to modify their building systems in order to include signatures in their packages. Digest lists aim at mitigating these issues. A digest list is a list of digests that are taken by IMA as reference measurements and loaded before files are accessed. Then, IMA compares calculated digests of accessed files with digests from loaded digest lists. If the digest is found, measurement, appraisal and audit are not performed. Multiple digest lists can be loaded at the same time, by providing to IMA metadata for each list: digest, signature and path. The digest is specified so that loaded digest lists can be identified only with the measurement of metadata. The signature is used for appraisal. If the verification succeeds, IMA loads the digest list even if security.ima is missing. Digest lists address the first issue because the TPM is used only if the digest of a measured file is unknown. On a minimal system, 10 of 1400 measurements are unknown because of mutable files (e.g. log files). Digest lists mitigate the second issue because, since digest lists do not change, they don't have to be sent at every remote attestation. Sending unknown measurements and a reference to digest lists would be sufficient. Finally, digest lists address also the third issue because Linux distribution vendors already provide the digests of files included in each RPM package. The digest list is stored in the RPM header, signed by the vendor. When using digest lists, a limitation must be considered. Since a measurement is not reported if the digest of an accessed file is found in a digest list, the measurement list does not show which files have been actually accessed, and in which sequence. A possible solution would be to load a list with digest of files which are usually accessed. Also, it is possible to selectively enable digest list lookup only for a subset of IMA policy rules. For example, a policy could enable digest lookup only for file accesses from the TCB and disable it for execve() and mmap() from regular users. Changelog v1: - added new policy option digest_list to selectively enable digest lookup - added support for appraisal - added support for immutable/mutable files Roberto Sassu (15): ima: generalize ima_read_policy() ima: generalize ima_write_policy() ima: generalize policy file operations ima: use ima_show_htable_value to show hash table data ima: add functions to manage digest lists ima: add parser of digest lists metadata ima: add parser of compact digest list ima: add parser of RPM package headers ima: introduce securityfs interfaces for digest lists ima: disable digest lookup if digest lists are not checked ima: add policy action digest_list ima: do not update security.ima if appraisal status is not INTEGRITY_PASS evm: add kernel command line option to select protected xattrs ima: add support for appraisal with digest lists ima: add Documentation/security/IMA-digest-lists.txt Documentation/admin-guide/kernel-parameters.txt | 4 + Documentation/security/IMA-digest-lists.txt | 161 include/linux/evm.h | 6 + include/linux/fs.h | 2 + security/integrity/evm/evm_main.c | 36 +++ security/integrity/iint.c | 1 + security/integrity/ima/Kconfig | 19 ++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h| 33 ++- security/integrity/ima/ima_api.c| 7 +- security/integrity/ima/ima_appraise.c | 52 +++- security/integrity/ima/ima_digest_list.c| 326 security/integrity/ima/ima_fs.c | 181 - security/integrity/ima/ima_main.c | 47 +++- security/integrity/ima/ima_policy.c | 33 ++- security/integrity/ima/ima_queue.c | 42 +++ security/integrity/integrity.h | 11 + 17 files changed, 877
Re: [Linux-ima-devel] [PATCH, RESEND 08/12] ima: added parser for RPM data type
On 8/10/2017 3:12 PM, Mimi Zohar wrote: On Wed, 2017-08-09 at 19:18 +0200, Roberto Sassu wrote: On 8/9/2017 4:30 PM, Mimi Zohar wrote: On Wed, 2017-08-09 at 11:15 +0200, Roberto Sassu wrote: On 8/2/2017 9:22 AM, James Morris wrote: On Tue, 1 Aug 2017, Roberto Sassu wrote: On 8/1/2017 12:27 PM, Christoph Hellwig wrote: On Tue, Aug 01, 2017 at 12:20:36PM +0200, Roberto Sassu wrote: This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Err, parsing arbitrary file formats has no business in the kernel. The benefit of this choice is that no actions are required for Linux distribution vendors to support the solution I'm proposing, because they already provide signed digest lists (RPM headers). Since the proof of loading a digest list is the digest of the digest list (included in the list metadata), if RPM headers are converted to a different format, remote attestation verifiers cannot check the signature. If the concern is security, it would be possible to prevent unsigned RPM headers from being parsed, if the PGP key type is upstreamed (adding in CC keyri...@vger.kernel.org). It's a security concern and also a layering violation, there should be no need to parse package file formats in the kernel. Parsing RPMs is not strictly necessary. Digests from the headers can be extracted and written to a new file using the compact data format (introduced with patch 7/12). At boot time, IMA measures this file before digests are uploaded to the kernel. At this point, only files with unknown digest will be added to the measurement list. At verification time, verifiers recreate the measurement list by merging together the digests uploaded to the kernel with the unknown digests. Then, they verify the obtained list. There are two ways to verify the digests: searching them in a reference database, or checking a signature. With the 'ima-sig' measurement list template, it is possible to verify signatures for each accessed file. With this patch set, it is possible to verify the signature of the file containing the digests uploaded to the kernel. If the data format changes, the signature cannot be verified. To avoid this limitation, the parsers could be moved to a userspace tool which then uploads the parsed digests to the kernel. IMA would measure the original files. But, if the tool is compromised, it could load digests not included in the parsed files. With the current solution this problem does not arise because no changes can be done by userspace applications to the uploaded data while digests are parsed by IMA. I could remove the RPM parser from the patch set for now. Is the remaining part of the patch set ok, and is the explanation of what it does clear? From a trusted boot perspective, file measurements are added to the measurement list, before access to the file is given. The measurement list contains ALL measurements, as defined by policy. This patch set changes that meaning to be all measurements, as defined by policy, with the exception of those in a white list. The digest list is also measured, so the measurement list is complete. Verifiers have to check the digest of digest lists. Otherwise, they would get an unknown digest and conclude that the system being verified has been compromised. Your proposal is basically a pre-approved "batched" measurement, of a set of known good measurements, without the corresponding list of measurements that this "batched" measurement represents. Right? Yes, correct. This pre-approved "batched" measurement represents not what has been accessed/executed on the system, but what potentially could be accessed/executed. That's a major difference. If you prefer, I could add a new policy rule option to avoid file measurements if the digest is in the digest list. Huh? Patch "ima: don't report measurements if digests are included in the loaded lists" is already doing this. Since the content of the measurement list depends on the policy, adding a new option would give a better understanding of what the measurement list represents. But, I agree that this is redundant. Changing the fundamental meaning of the measurement list is not acceptable. You could define a new securityfs file to differentiate between the full measurement list and this abbreviated one. But There cannot be two measurement lists at the same time. Providing the full measurement list (containing the digest of files being accessed) implies that its integrity must be protected with PCR extends, making the optimization done by this patch set useless. True, so you would be able to configure the system with
Re: [Linux-ima-devel] [PATCH 11/12] ima: don't report measurements if digests are included in the loaded lists
On 8/9/2017 10:36 PM, Ken Goldman wrote: On 7/25/2017 11:44 AM, Roberto Sassu wrote: Don't report measurements if the file digest has been included in an uploaded digest list. The advantage of this solution is that the boot time overhead, when a TPM is available, is very small because a PCR is extended only for unknown files. The disadvantage is that verifiers do not know anymore which and when files are accessed (they must assume that the worst case happened, i.e. all files have been accessed). Am I reading this correctly that you want to measure certain files, but not ones that have been included in a "digest list", which sounds like a white list of sorts. If so, I have two concerns: 1 - How would the client get this digest list? Shouldn't it be up to the relying party to decide what is trusted and not trusted, not the client? The client can get the digest list from the RPM database or the measurement list of a previous boot. A verifier still decides if the client is trusted or not, depending on what could be possibly accessed rather than what has been accessed. What of the case with two different relying parties that have a different list of trusted applications? E.g., one trusts any version of program X, while the other trusts only version 3.1 and up? There are two ways to provide more accurate information (make the list of possibly accessed files closer to the list of accessed files): 1) use the measurement list of a previous boot as a source for the digest list 2) use the RPM database as a source for the digest lists, and load the headers of the latest version of RPMs and: load only the headers of RPMs including files that appear in the measurement list of a previous boot (this solution should be preferred to solution 1, as signature-based attestation can be done) 2 - What about files on the digest list that were not run? The relying party may want to know if a program wasn't run? E.g., antivirus or a firewall. If the rule is "don't measure if it's on the digest list", how does the relying party know if it was run? If a measurement list does not contain unknown digests, this means that no malicious software was loaded. But, if the measurement list contains the digest of an antivirus binary, this does not necessarily mean that it was loaded. Since PCR 10 can be extended from userspace, a malicious user can extend the PCR with the digest of a fake measurement entry reporting the loading of the antivirus. He can then add the fake entry to the measurement list before it is sent to verifiers, so that they won't notice the addition. A possible countermeasure to this attack would be to modify the TPM driver to refuse to extend the PCRs used by IMA. Then, IMA could be further extended to accept a list of digests of files that will be certainly used and to create a measurement entry only after all files have been accessed. Roberto -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [Linux-ima-devel] [PATCH, RESEND 08/12] ima: added parser for RPM data type
On 8/2/2017 9:22 AM, James Morris wrote: On Tue, 1 Aug 2017, Roberto Sassu wrote: On 8/1/2017 12:27 PM, Christoph Hellwig wrote: On Tue, Aug 01, 2017 at 12:20:36PM +0200, Roberto Sassu wrote: This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Err, parsing arbitrary file formats has no business in the kernel. The benefit of this choice is that no actions are required for Linux distribution vendors to support the solution I'm proposing, because they already provide signed digest lists (RPM headers). Since the proof of loading a digest list is the digest of the digest list (included in the list metadata), if RPM headers are converted to a different format, remote attestation verifiers cannot check the signature. If the concern is security, it would be possible to prevent unsigned RPM headers from being parsed, if the PGP key type is upstreamed (adding in CC keyri...@vger.kernel.org). It's a security concern and also a layering violation, there should be no need to parse package file formats in the kernel. Parsing RPMs is not strictly necessary. Digests from the headers can be extracted and written to a new file using the compact data format (introduced with patch 7/12). At boot time, IMA measures this file before digests are uploaded to the kernel. At this point, only files with unknown digest will be added to the measurement list. At verification time, verifiers recreate the measurement list by merging together the digests uploaded to the kernel with the unknown digests. Then, they verify the obtained list. There are two ways to verify the digests: searching them in a reference database, or checking a signature. With the 'ima-sig' measurement list template, it is possible to verify signatures for each accessed file. With this patch set, it is possible to verify the signature of the file containing the digests uploaded to the kernel. If the data format changes, the signature cannot be verified. To avoid this limitation, the parsers could be moved to a userspace tool which then uploads the parsed digests to the kernel. IMA would measure the original files. But, if the tool is compromised, it could load digests not included in the parsed files. With the current solution this problem does not arise because no changes can be done by userspace applications to the uploaded data while digests are parsed by IMA. I could remove the RPM parser from the patch set for now. Is the remaining part of the patch set ok, and is the explanation of what it does clear? Thanks Roberto I'm not really clear on exactly how this patch series works. Can you provide a more concrete explanation of what steps would occur during boot and attestation? -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [Linux-ima-devel] [PATCH, RESEND 08/12] ima: added parser for RPM data type
On 8/2/2017 9:22 AM, James Morris wrote: On Tue, 1 Aug 2017, Roberto Sassu wrote: On 8/1/2017 12:27 PM, Christoph Hellwig wrote: On Tue, Aug 01, 2017 at 12:20:36PM +0200, Roberto Sassu wrote: This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Err, parsing arbitrary file formats has no business in the kernel. The benefit of this choice is that no actions are required for Linux distribution vendors to support the solution I'm proposing, because they already provide signed digest lists (RPM headers). Since the proof of loading a digest list is the digest of the digest list (included in the list metadata), if RPM headers are converted to a different format, remote attestation verifiers cannot check the signature. If the concern is security, it would be possible to prevent unsigned RPM headers from being parsed, if the PGP key type is upstreamed (adding in CC keyri...@vger.kernel.org). It's a security concern and also a layering violation, there should be no need to parse package file formats in the kernel. I'm not really clear on exactly how this patch series works. Can you provide a more concrete explanation of what steps would occur during boot and attestation? The main idea of this patch set is that, if a system executes or reads good files (e.g. those from a Linux distribution), the difference between the assertion 'a file could have possibly been accessed' and 'a file has been accessed' is not relevant for verifiers that only check the provenance of software. Then, for those verifiers, a measurement representing the list of good files which could have possibly been accessed gives the same guarantees of individual file measurements. The patch set introduces two data types: - digest list: contains the digests of good files - list metadata: contains the digest, the signature and the path of each digest list to load (why loading many lists instead of one will be clear after I explain the remote attestation verification process) Steps at boot: 1) systemd reads the path of list metadata from /etc/ima/digest-lists and writes it to a new securityfs file created by IMA 2) IMA reads and parses the list metadata (same mechanism for loading a policy, already upstreamed) 3) for each list metadata, IMA reads and parses the digest list and adds the file digests to a hash table 4) when a file is accessed, IMA calculates the digest as before and searches the file digest in the new hash table; if the digest is found, IMA sets the IMA_MEASURED flag in the inode metadata and clears the IMA_MEASURE action Notes: - list metadata and digest lists are measured before IMA reads them - the digest of digest lists is also added to the hash table, otherwise there would be a measurement for each digest list The measurement list looks like: 10 ima-ng boot_aggregate 10 ima-ng systemd (exe + libs) 10 ima-ng /etc/ima/digest-lists 10 ima-ng 10 ima-ng Steps during the verification: Case 1: list metadata and digest lists are provided to verifiers This is necessary when: - digest lists are not signed - verifiers do not trust the signer - verifiers want to perform more checks on digest lists (digest lists may contain digest of outdated software) Verifiers: 1) parse the list metadata received 2) for each digest list received, calculate the digest and compare it with the digest included in the list metadata 3) calculate the digest of list metadata and compare it with the digest in the measurement list 4) calculate the digest of the path of list metadata and compare it with the digest of /etc/ima/digest-lists in the measurement list 5) check boot_aggregate, systemd exe and libs, and unknown files 6) check the digest lists Case 2: only list metadata is provided to verifiers Verifiers: 1) parse the list metadata received 2) for each digest list, verify the signature 3) calculate the digest of the path of list metadata and compare it with the digest of /etc/ima/digest-lists in the measurement list 4) check boot_aggregate, systemd exe and libs, and unknown files In Case 2, the verification process is simplified, because if the signature of digest lists is valid, this means that possibly accessed files are provided by the signer. The problem here is that verifiers know the digest of possibly accessed files from the measurement done by IMA at the time digest lists are read. If IMA cannot parse the original (signed) digest list, it would measure something that cannot be verified with the signature. RPM-based distributions already provide signed digest lists (the RPM headers
Re: [PATCH, RESEND 08/12] ima: added parser for RPM data type
On 8/1/2017 12:27 PM, Christoph Hellwig wrote: On Tue, Aug 01, 2017 at 12:20:36PM +0200, Roberto Sassu wrote: This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Err, parsing arbitrary file formats has no business in the kernel. The benefit of this choice is that no actions are required for Linux distribution vendors to support the solution I'm proposing, because they already provide signed digest lists (RPM headers). Since the proof of loading a digest list is the digest of the digest list (included in the list metadata), if RPM headers are converted to a different format, remote attestation verifiers cannot check the signature. If the concern is security, it would be possible to prevent unsigned RPM headers from being parsed, if the PGP key type is upstreamed (adding in CC keyri...@vger.kernel.org). Roberto -- HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063 Managing Director: Bo PENG, Qiuen PENG, Shengli WANG -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH, RESEND 08/12] ima: added parser for RPM data type
This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_digest_list.c | 86 +++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index c1ef79a..0b5916d 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -19,11 +19,13 @@ #include "ima.h" #include "ima_template_lib.h" +#define RPMTAG_FILEDIGESTS 1035 + enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, DATA__LAST}; -enum digest_data_types {DATA_TYPE_COMPACT_LIST}; +enum digest_data_types {DATA_TYPE_COMPACT_LIST, DATA_TYPE_RPM}; enum compact_list_entry_ids {COMPACT_LIST_ID_DIGEST}; @@ -33,6 +35,20 @@ struct compact_list_hdr { u32 datalen; } __packed; +struct rpm_hdr { + u32 magic; + u32 reserved; + u32 tags; + u32 datasize; +} __packed; + +struct rpm_entryinfo { + int32_t tag; + u32 type; + int32_t offset; + u32 count; +} __packed; + static int ima_parse_compact_list(loff_t size, void *buf) { void *bufp = buf, *bufendp = buf + size; @@ -80,6 +96,71 @@ static int ima_parse_compact_list(loff_t size, void *buf) return 0; } +static int ima_parse_rpm(loff_t size, void *buf) +{ + void *bufp = buf, *bufendp = buf + size; + struct rpm_hdr *hdr = bufp; + u32 tags = be32_to_cpu(hdr->tags); + struct rpm_entryinfo *entry; + void *datap = bufp + sizeof(*hdr) + tags * sizeof(struct rpm_entryinfo); + int digest_len = hash_digest_size[ima_hash_algo]; + u8 digest[digest_len]; + int ret, i, j; + + const unsigned char rpm_header_magic[8] = { + 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + + if (size < sizeof(*hdr)) { + pr_err("Missing RPM header\n"); + return -EINVAL; + } + + if (memcmp(bufp, rpm_header_magic, sizeof(rpm_header_magic))) { + pr_err("Invalid RPM header\n"); + return -EINVAL; + } + + bufp += sizeof(*hdr); + + for (i = 0; i < tags && (bufp + sizeof(*entry)) <= bufendp; +i++, bufp += sizeof(*entry)) { + entry = bufp; + + if (be32_to_cpu(entry->tag) != RPMTAG_FILEDIGESTS) + continue; + + datap += be32_to_cpu(entry->offset); + + for (j = 0; j < be32_to_cpu(entry->count) && +datap < bufendp; j++) { + if (strlen(datap) == 0) { + datap++; + continue; + } + + if (datap + digest_len * 2 + 1 > bufendp) { + pr_err("RPM header read at invalid offset\n"); + return -EINVAL; + } + + ret = hex2bin(digest, datap, digest_len); + if (ret < 0) + return -EINVAL; + + ret = ima_add_digest_data_entry(digest); + if (ret < 0 && ret != -EEXIST) + return ret; + + datap += digest_len * 2 + 1; + } + + break; + } + + return 0; +} + static int ima_parse_digest_list_data(struct ima_field_data *data) { void *digest_list; @@ -107,6 +188,9 @@ static int ima_parse_digest_list_data(struct ima_field_data *data) case DATA_TYPE_COMPACT_LIST: ret = ima_parse_compact_list(digest_list_size, digest_list); break; + case DATA_TYPE_RPM: + ret = ima_parse_rpm(digest_list_size, digest_list); + break; default: pr_err("Parser for data type %d not implemented\n", data_type); ret = -EINVAL; -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH, RESEND 06/12] ima: added parser of digest lists metadata
Userspace applications will be able to load digest lists by supplying their metadata. Digest list metadata are: - DATA_ALGO: algorithm of the digests to be uploaded - DATA_DIGEST: digest of the file containing the digest list - DATA_SIGNATURE: signature of the file containing the digest list - DATA_FILE_PATH: pathname - DATA_REF_ID: reference ID of the digest list - DATA_TYPE: type of digest list The new function ima_parse_digest_list_metadata() parses the metadata and load each file individually. Then, it parses the data according to the data type specified. Since digest lists are measured, their digest is added to the hash table so that IMA does not create a measurement entry for them (which would affect the performance). The only measurement entry created will be for the metadata. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- include/linux/fs.h | 1 + security/integrity/ima/Kconfig | 11 security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 8 +++ security/integrity/ima/ima_digest_list.c | 105 +++ 5 files changed, 126 insertions(+) create mode 100644 security/integrity/ima/ima_digest_list.c diff --git a/include/linux/fs.h b/include/linux/fs.h index 6e1fd5d..2eb6e7c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2751,6 +2751,7 @@ extern int do_pipe_flags(int *, int); id(KEXEC_IMAGE, kexec-image)\ id(KEXEC_INITRAMFS, kexec-initramfs)\ id(POLICY, security-policy) \ + id(DIGEST_LIST, security-digest-list) \ id(MAX_ID, ) #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 35ef693..8965dcc 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -227,3 +227,14 @@ config IMA_APPRAISE_SIGNED_INIT default n help This option requires user-space init to be signed. + +config IMA_DIGEST_LIST + bool "Measure files depending on uploaded digest lists" + depends on IMA + default n + help + This option allows users to load digest lists. If a measured + file has the same digest of one from loaded lists, IMA will + not create a new measurement entry. A measurement entry will + be created only when digest lists are loaded (this entry + contains the digest of digest lists metadata). diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 29f198b..00dbe3a 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -9,4 +9,5 @@ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o +ima-$(CONFIG_IMA_DIGEST_LIST) += ima_digest_list.o obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a0c6808..9ecb7cc 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -157,6 +157,14 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); struct ima_digest *ima_lookup_loaded_digest(u8 *digest); int ima_add_digest_data_entry(u8 *digest); +#ifdef CONFIG_IMA_DIGEST_LIST +ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf); +#else +static inline ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf) +{ + return -ENOTSUPP; +} +#endif int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c new file mode 100644 index 000..3e1ff69b --- /dev/null +++ b/security/integrity/ima/ima_digest_list.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Huawei Technologies Co. Ltd. + * + * Author: Roberto Sassu <roberto.sa...@huawei.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_digest_list.c + * Functions to manage digest lists. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "ima.h" +#include "ima_template_lib.h" + +enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, +DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, +DATA__LAST}; + +static int ima_parse_digest_list_data(struct ima_field_data *data) +{ + void *digest_list; + loff_t digest_list_size; + u16 data_algo = le16_to_cpu(*(u16 *)data[D
[PATCH 12/12] ima: added Documentation/security/IMA-digest-lists.txt
This patch adds the documentation of the new IMA feature, to load and measure file digest lists. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- Documentation/security/IMA-digest-lists.txt | 150 1 file changed, 150 insertions(+) create mode 100644 Documentation/security/IMA-digest-lists.txt diff --git a/Documentation/security/IMA-digest-lists.txt b/Documentation/security/IMA-digest-lists.txt new file mode 100644 index 000..f9eed21 --- /dev/null +++ b/Documentation/security/IMA-digest-lists.txt @@ -0,0 +1,150 @@ +File Digest Lists + + INTRODUCTION + +IMA, for each file matching policy rules, calculates a digest, creates +a new entry in the measurement list and extends a TPM PCR with the digest +of entry data. The last step causes a noticeable performance reduction. + +Since systems likely access the same files, repeating the above tasks at +every boot can be avoided by replacing individual measurements of likely +accessed files with only one measurement of their digests: the advantage +is that the system performance significantly improves due to less PCR +extend operations; on the other hand, the information about which files +have exactly been accessed and in which sequence is lost. + +If this new measurement reports only good digests (e.g. those of +files included in a Linux distribution), and if verifiers only check +that a system executed good software and didn't access malicious data, +the disadvantages reported earlier would be acceptable. + +The Trusted Computing paradigm measure & load is still respected by IMA +with the proposed optimization. If a file being accessed is not in a +measured digest list, a measurement will be recorded as before. If it is, +the list has already been measured, and the verifier must assume that +files with digest in the list have been accessed. + +Measuring digest lists gives the following benefits: + +- boot time reduction + For a minimal Linux installation with 1400 measurements, the boot time + decreases from 1 minute 30 seconds to 15 seconds, after loading to IMA + the digest of all files packaged by the distribution (32000). The new + list contains 92 entries. Without IMA, the boot time is 8.5 seconds. + +- lower network and CPU requirements for remote attestation + With the IMA optimization, both the measurement and digest lists + must be verified for a complete evaluation. However, since the lists + are fixed, they could be sent to and checked by the verifier only once. + Then, during a remote attestation, the only remaining task is to verify + the short measurement list. + +- signature-based remote attestation + Digest list signature can be used as a proof of the provenance for the + files whose digest is in the list. Then, if verifiers trust the signer + and only check provenance, remote attestation verification would simply + consist on checking digest lists signatures and that the measurement + list only contain list metadata digests (reference measurement databases + would be no longer required). An example of a signed digest list, + that can be parsed with this patch set, is the RPM package header. + +Digest lists are loaded in two stages by IMA through the new securityfs +interface called 'digest_lists'. Users supply metadata, for the digest +lists they want to load: path, format, digest, signature and algorithm +of the digest. + +Then, after the metadata digest is added to the measurement list, IMA +reads the digest lists at the path specified and loads the digests in +a hash table (digest lists are not measured, since their digest is already +included in the metadata). With metadata measurement instead of digest list +measurement, it is possible to avoid a performance reduction that would +occur by measuring many digest lists (e.g. RPM headers) individually. +If, alternatively, digest lists are loaded together, their signature +cannot be verified. + +Lastly, when a file is accessed, IMA searches the calculated digest in +the hash table. Only if the digest is not found a new entry is added +to the measurement list. + + + + FORMAT + +The format of digest list metadata is: + +algo[2] digest_len[4] digest[digest_len] +signature_len[4] signature[signature_len] +path_len[4] path[path_len] +ref_id_len[4] ref_id[ref_id_len] +list_type_len[4] list_type[list_type_len] + +algo, list_type and _len are little endian. + + +algo values are defined in include/uapi/linux/hash_info.h. The algorithms +in the list metadata must be the same of ima_hash_algo (algorithm used +by IMA to calculate the file digest). + +list type values: + +0: compact digest list +1: RPM package header + + +The format of the compact digest list is: + +entry_id[2] count[4] data_len[4] +data[data_len] +[...] +entry_id[2] count[4] data_len[4] +data[data_len] + +entry_id, count and data_len are little endian. + +At the moment, entry_id can have valu
[PATCH 11/12] ima: don't report measurements if digests are included in the loaded lists
Don't report measurements if the file digest has been included in an uploaded digest list. The advantage of this solution is that the boot time overhead, when a TPM is available, is very small because a PCR is extended only for unknown files. The disadvantage is that verifiers do not know anymore which and when files are accessed (they must assume that the worst case happened, i.e. all files have been accessed). Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_main.c | 8 1 file changed, 8 insertions(+) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c329549..e289b7c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -253,6 +253,14 @@ static int process_measurement(struct file *file, char *buf, loff_t size, goto out_digsig; } + if (!ima_disable_digest_check) { + if (ima_lookup_loaded_digest(iint->ima_hash->digest)) { + action ^= IMA_MEASURE; + iint->flags |= IMA_MEASURED; + iint->measured_pcrs |= (0x1 << pcr); + } + } + if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(>f_path, , filename); -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 10/12] ima: disable digest lookup if digest lists are not measured
Loading digest lists affects the behavior of IMA, as files whose digest has been uploaded will not be displayed in the measurement list. If the digest lists loading event is not reported, verifiers would believe that the files with uploaded digests have not been accessed. To prevent this, the DIGEST_CHECK hook has been defined and a new rule to measure files accessed by the new hook has been added to the default policy. If the currently loaded policy does not contain that rule, digest lookup is disabled. Digest lookup is also disabled if CONFIG_IMA_DIGEST_LIST is not defined. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima.h| 1 + security/integrity/ima/ima_main.c | 15 ++- security/integrity/ima/ima_policy.c | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 77dd4d0..2a558ee 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -199,6 +199,7 @@ static inline unsigned long ima_hash_key(u8 *digest) hook(KEXEC_KERNEL_CHECK)\ hook(KEXEC_INITRAMFS_CHECK) \ hook(POLICY_CHECK) \ + hook(DIGEST_LIST_CHECK) \ hook(MAX_CHECK) #define __ima_hook_enumify(ENUM) ENUM, diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 2aebb79..c329549 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -29,6 +29,12 @@ int ima_initialized; +#ifdef CONFIG_IMA_DIGEST_LIST +static int ima_disable_digest_check; +#else +static int ima_disable_digest_check = 1; +#endif + #ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; #else @@ -171,6 +177,9 @@ static int process_measurement(struct file *file, char *buf, loff_t size, bool violation_check; enum hash_algo hash_algo; + if (func == DIGEST_LIST_CHECK && !ima_policy_flag) + ima_disable_digest_check = 1; + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; @@ -181,6 +190,9 @@ static int process_measurement(struct file *file, char *buf, loff_t size, action = ima_get_action(inode, mask, func, ); violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); + if (func == DIGEST_LIST_CHECK && !(action & IMA_MEASURE)) + ima_disable_digest_check = 1; + if (!action && !violation_check) return 0; @@ -375,7 +387,8 @@ static int read_idmap[READING_MAX_ID] = { [READING_MODULE] = MODULE_CHECK, [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, - [READING_POLICY] = POLICY_CHECK + [READING_POLICY] = POLICY_CHECK, + [READING_DIGEST_LIST] = DIGEST_LIST_CHECK }; /** diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 95209a5..b5c004d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -127,6 +127,7 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 09/12] ima: introduce securityfs interfaces for digest lists
This patch introduces the file 'digest_lists' in the securityfs filesystem, to load digest lists metadata. IMA will parse the metadata and loads the digest lists from the path provided. It also introduces 'digests_count', to show the number of digests stored in the digest hash table. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index ad3d674..08174c1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -34,11 +34,15 @@ static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; static struct dentry *ima_policy; +static struct dentry *digest_lists; +static struct dentry *digests_count; static enum kernel_read_file_id ima_get_file_id(struct dentry *dentry) { if (dentry == ima_policy) return READING_POLICY; + else if (dentry == digest_lists) + return READING_DIGEST_LIST; return READING_UNKNOWN; } @@ -66,6 +70,8 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, val = _htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) val = _htable.len; + else if (filp->f_path.dentry == digests_count) + val = _digests_htable.len; len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); @@ -301,6 +307,9 @@ static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) pr_debug("rule: %s\n", p); rc = ima_parse_add_rule(p); + } else if (file_id == READING_DIGEST_LIST) { + rc = ima_parse_digest_list_metadata(size, datap); + datap += rc; } if (rc < 0) break; @@ -510,8 +519,22 @@ int __init ima_fs_init(void) if (IS_ERR(ima_policy)) goto out; +#ifdef CONFIG_IMA_DIGEST_LIST + digest_lists = securityfs_create_file("digest_lists", S_IWUSR, ima_dir, + NULL, _data_upload_ops); + if (IS_ERR(digest_lists)) + goto out; + + digests_count = securityfs_create_file("digests_count", + S_IRUSR | S_IRGRP, ima_dir, + NULL, _htable_value_ops); + if (IS_ERR(digests_count)) + goto out; +#endif return 0; out: + securityfs_remove(digests_count); + securityfs_remove(digest_lists); securityfs_remove(violations); securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements); -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/12] ima: added parser of digest lists metadata
Userspace applications will be able to load digest lists by supplying their metadata. Digest list metadata are: - DATA_ALGO: algorithm of the digests to be uploaded - DATA_DIGEST: digest of the file containing the digest list - DATA_SIGNATURE: signature of the file containing the digest list - DATA_FILE_PATH: pathname - DATA_REF_ID: reference ID of the digest list - DATA_TYPE: type of digest list The new function ima_parse_digest_list_metadata() parses the metadata and load each file individually. Then, it parses the data according to the data type specified. Since digest lists are measured, their digest is added to the hash table so that IMA does not create a measurement entry for them (which would affect the performance). The only measurement entry created will be for the metadata. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- include/linux/fs.h | 1 + security/integrity/ima/Kconfig | 11 security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 8 +++ security/integrity/ima/ima_digest_list.c | 105 +++ 5 files changed, 126 insertions(+) create mode 100644 security/integrity/ima/ima_digest_list.c diff --git a/include/linux/fs.h b/include/linux/fs.h index 6e1fd5d..2eb6e7c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2751,6 +2751,7 @@ extern int do_pipe_flags(int *, int); id(KEXEC_IMAGE, kexec-image)\ id(KEXEC_INITRAMFS, kexec-initramfs)\ id(POLICY, security-policy) \ + id(DIGEST_LIST, security-digest-list) \ id(MAX_ID, ) #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 35ef693..8965dcc 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -227,3 +227,14 @@ config IMA_APPRAISE_SIGNED_INIT default n help This option requires user-space init to be signed. + +config IMA_DIGEST_LIST + bool "Measure files depending on uploaded digest lists" + depends on IMA + default n + help + This option allows users to load digest lists. If a measured + file has the same digest of one from loaded lists, IMA will + not create a new measurement entry. A measurement entry will + be created only when digest lists are loaded (this entry + contains the digest of digest lists metadata). diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 29f198b..00dbe3a 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -9,4 +9,5 @@ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima_template.o ima_template_lib.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o +ima-$(CONFIG_IMA_DIGEST_LIST) += ima_digest_list.o obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a0c6808..77dd4d0 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -157,6 +157,14 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); struct ima_digest *ima_lookup_loaded_digest(u8 *digest); int ima_add_digest_data_entry(u8 *digest); +#ifdef CONFIG_IMA_DIGEST_LIST +ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf); +#else +static inline ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf) +{ + return -ENOTSUP; +} +#endif int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c new file mode 100644 index 000..3e1ff69b --- /dev/null +++ b/security/integrity/ima/ima_digest_list.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Huawei Technologies Co. Ltd. + * + * Author: Roberto Sassu <roberto.sa...@huawei.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: ima_digest_list.c + * Functions to manage digest lists. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include "ima.h" +#include "ima_template_lib.h" + +enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, +DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, +DATA__LAST}; + +static int ima_parse_digest_list_data(struct ima_field_data *data) +{ + void *digest_list; + loff_t digest_list_size; + u16 data_algo = le16_to_cpu(*(u16 *)data[D
[PATCH 08/12] ima: added parser for RPM data type
This patch introduces a parser for RPM packages. It extracts the digests from the RPMTAG_FILEDIGESTS header section and converts them to binary data before adding them to the hash table. The advantage of this data type is that verifiers can determine who produced that data, as headers are signed by Linux distributions vendors. RPM headers signatures can be provided as digest list metadata. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_digest_list.c | 84 +++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index c1ef79a..11ee77e 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -19,11 +19,13 @@ #include "ima.h" #include "ima_template_lib.h" +#define RPMTAG_FILEDIGESTS 1035 + enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE, DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE, DATA__LAST}; -enum digest_data_types {DATA_TYPE_COMPACT_LIST}; +enum digest_data_types {DATA_TYPE_COMPACT_LIST, DATA_TYPE_RPM}; enum compact_list_entry_ids {COMPACT_LIST_ID_DIGEST}; @@ -33,6 +35,20 @@ struct compact_list_hdr { u32 datalen; } __packed; +struct rpm_hdr { + u32 magic; + u32 reserved; + u32 tags; + u32 datasize; +} __packed; + +struct rpm_entryinfo { + int32_t tag; + u32 type; + int32_t offset; + u32 count; +} __packed; + static int ima_parse_compact_list(loff_t size, void *buf) { void *bufp = buf, *bufendp = buf + size; @@ -80,6 +96,69 @@ static int ima_parse_compact_list(loff_t size, void *buf) return 0; } +static int ima_parse_rpm(loff_t size, void *buf) +{ + void *bufp = buf, *bufendp = buf + size; + struct rpm_hdr *hdr = bufp; + u32 tags = be32_to_cpu(hdr->tags); + struct rpm_entryinfo *entry; + void *datap = bufp + sizeof(*hdr) + tags * sizeof(struct rpm_entryinfo); + int digest_len = hash_digest_size[ima_hash_algo]; + u8 digest[digest_len]; + int ret, i, j; + + const unsigned char rpm_header_magic[8] = { + 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + + if (size < sizeof(*hdr)) { + pr_err("Missing RPM header\n"); + return -EINVAL; + } + + if (memcmp(bufp, rpm_header_magic, sizeof(rpm_header_magic))) { + pr_err("Invalid RPM header\n"); + return -EINVAL; + } + + bufp += sizeof(*hdr); + + for (i = 0; i < tags && (bufp + sizeof(*entry)) <= bufendp; +i++, bufp += sizeof(*entry)) { + entry = bufp; + + if (be32_to_cpu(entry->tag) != RPMTAG_FILEDIGESTS) + continue; + + datap += be32_to_cpu(entry->offset); + + for (j = 0; j < be32_to_cpu(entry->count) && +datap < bufendp; j++) { + if (strlen(datap) == 0) { + datap++; + continue; + } + + if (datap + digest_len * 2 + 1 > bufendp) { + pr_err("RPM header read at invalid offset\n"); + return -EINVAL; + } + + hex2bin(digest, datap, digest_len); + + ret = ima_add_digest_data_entry(digest); + if (ret < 0 && ret != -EEXIST) + return ret; + + datap += digest_len * 2 + 1; + } + + break; + } + + return 0; +} + static int ima_parse_digest_list_data(struct ima_field_data *data) { void *digest_list; @@ -107,6 +186,9 @@ static int ima_parse_digest_list_data(struct ima_field_data *data) case DATA_TYPE_COMPACT_LIST: ret = ima_parse_compact_list(digest_list_size, digest_list); break; + case DATA_TYPE_RPM: + ret = ima_parse_rpm(digest_list_size, digest_list); + break; default: pr_err("Parser for data type %d not implemented\n", data_type); ret = -EINVAL; -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 00/12] ima: measure digest lists instead of individual files
This patch set applies on top of kernel v4.13-rc2. IMA, for each file matching policy rules, calculates a digest, creates a new entry in the measurement list and extends a TPM PCR with the digest of entry data. The last step causes a noticeable performance reduction. Since systems likely access the same files, repeating the above tasks at every boot can be avoided by replacing individual measurements of likely accessed files with only one measurement of their digests: the advantage is that the system performance significantly improves due to less PCR extend operations; on the other hand, the information about which files have exactly been accessed and in which sequence is lost. If this new measurement reports only good digests (e.g. those of files included in a Linux distribution), and if verifiers only check that a system executed good software and didn't access malicious data, the disadvantages reported earlier would be acceptable. The Trusted Computing paradigm measure & load is still respected by IMA with the proposed optimization. If a file being accessed is not in a measured digest list, a measurement will be recorded as before. If it is, the list has already been measured, and the verifier must assume that files with digest in the list have been accessed. Measuring digest lists gives the following benefits: - boot time reduction For a minimal Linux installation with 1400 measurements, the boot time decreases from 1 minute 30 seconds to 15 seconds, after loading to IMA the digest of all files packaged by the distribution (32000). The new list contains 92 entries. Without IMA, the boot time is 8.5 seconds. - lower network and CPU requirements for remote attestation With the IMA optimization, both the measurement and digest lists must be verified for a complete evaluation. However, since the lists are fixed, they could be sent to and checked by the verifier only once. Then, during a remote attestation, the only remaining task is to verify the short measurement list. - signature-based remote attestation Digest list signature can be used as a proof of the provenance for the files whose digest is in the list. Then, if verifiers trust the signer and only check provenance, remote attestation verification would simply consist on checking digest lists signatures and that the measurement list only contain list metadata digests (reference measurement databases would be no longer required). An example of a signed digest list, that can be parsed with this patch set, is the RPM package header. Digest lists are loaded in two stages by IMA through the new securityfs interface called 'digest_lists'. Users supply metadata, for the digest lists they want to load: path, format, digest, signature and algorithm of the digest. Then, after the metadata digest is added to the measurement list, IMA reads the digest lists at the path specified and loads the digests in a hash table (digest lists are not measured, since their digest is already included in the metadata). With metadata measurement instead of digest list measurement, it is possible to avoid a performance reduction that would occur by measuring many digest lists (e.g. RPM headers) individually. If, alternatively, digest lists are loaded together, their signature cannot be verified. Lastly, when a file is accessed, IMA searches the calculated digest in the hash table. Only if the digest is not found a new entry is added to the measurement list. Roberto Sassu (12): ima: generalize ima_read_policy() ima: generalize ima_write_policy() ima: generalize policy file operations ima: use ima_show_htable_value to show hash table data ima: add functions to manage digest lists ima: added parser of digest lists metadata ima: added parser for compact digest list ima: added parser for RPM data type ima: introduce securityfs interfaces for digest lists ima: disable digest lookup if digest lists are not measured ima: don't report measurements if digests are included in the loaded lists ima: added Documentation/security/IMA-digest-lists.txt Documentation/security/IMA-digest-lists.txt | 150 + include/linux/fs.h | 1 + security/integrity/ima/Kconfig | 11 ++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h| 17 ++ security/integrity/ima/ima_digest_list.c| 247 security/integrity/ima/ima_fs.c | 178 security/integrity/ima/ima_main.c | 23 ++- security/integrity/ima/ima_policy.c | 1 + security/integrity/ima/ima_queue.c | 39 + 10 files changed, 602 insertions(+), 66 deletions(-) create mode 100644 Documentation/security/IMA-digest-lists.txt create mode 100644 security/integrity/ima/ima_digest_list.c -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the bod
[PATCH 05/12] ima: add functions to manage digest lists
This patch first introduces a new structure called ima_digest, which will contain a digest parsed from the digest list. It has been preferred to ima_queue_entry, as the existing structure includes an additional member (a list head), which is not necessary for digest lookup. Then, this patch introduces functions to lookup and add a digest to a hash table, which will be used by the parsers. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima.h | 8 security/integrity/ima/ima_queue.c | 39 ++ 2 files changed, 47 insertions(+) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d52b487..a0c6808 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -107,6 +107,11 @@ struct ima_queue_entry { }; extern struct list_head ima_measurements; /* list of all measurements */ +struct ima_digest { + struct hlist_node hnext; + u8 digest[0]; +}; + /* Some details preceding the binary serialized measurement list */ struct ima_kexec_hdr { u16 version; @@ -150,6 +155,8 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); struct ima_template_desc *ima_template_desc_current(void); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); +struct ima_digest *ima_lookup_loaded_digest(u8 *digest); +int ima_add_digest_data_entry(u8 *digest); int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); @@ -166,6 +173,7 @@ struct ima_h_table { struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; }; extern struct ima_h_table ima_htable; +extern struct ima_h_table ima_digests_htable; static inline unsigned long ima_hash_key(u8 *digest) { diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index a02a86d..d1a3d3f 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -42,6 +42,11 @@ struct ima_h_table ima_htable = { .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT }; +struct ima_h_table ima_digests_htable = { + .len = ATOMIC_LONG_INIT(0), + .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT +}; + /* mutex protects atomicity of extending measurement list * and extending the TPM PCR aggregate. Since tpm_extend can take * long (and the tpm driver uses a mutex), we can't use the spinlock. @@ -212,3 +217,37 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry) mutex_unlock(_extend_list_mutex); return result; } + +struct ima_digest *ima_lookup_loaded_digest(u8 *digest) +{ + struct ima_digest *d = NULL; + int digest_len = hash_digest_size[ima_hash_algo]; + unsigned int key = ima_hash_key(digest); + + rcu_read_lock(); + hlist_for_each_entry_rcu(d, _digests_htable.queue[key], hnext) { + if (memcmp(d->digest, digest, digest_len) == 0) + break; + } + rcu_read_unlock(); + return d; +} + +int ima_add_digest_data_entry(u8 *digest) +{ + struct ima_digest *d = ima_lookup_loaded_digest(digest); + int digest_len = hash_digest_size[ima_hash_algo]; + unsigned int key = ima_hash_key(digest); + + if (d) + return -EEXIST; + + d = kmalloc(sizeof(*d) + digest_len, GFP_KERNEL); + if (d == NULL) + return -ENOMEM; + + memcpy(d->digest, digest, digest_len); + hlist_add_head_rcu(>hnext, _digests_htable.queue[key]); + atomic_long_inc(_digests_htable.len); + return 0; +} -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 04/12] ima: use ima_show_htable_value to show hash table data
This patch removes ima_show_htable_violations() and ima_show_measurements_count(). ima_show_htable_value(), called by those functions, determines which hash table data should be copied to the buffer depending on the dentry of the file passed as argument. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 38 -- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index f4199f2..ad3d674 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -55,38 +55,24 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup); static int valid_policy = 1; #define TMPBUFLEN 12 -static ssize_t ima_show_htable_value(char __user *buf, size_t count, -loff_t *ppos, atomic_long_t *val) +static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, +size_t count, loff_t *ppos) { + atomic_long_t *val = NULL; char tmpbuf[TMPBUFLEN]; ssize_t len; + if (filp->f_path.dentry == violations) + val = _htable.violations; + else if (filp->f_path.dentry == runtime_measurements_count) + val = _htable.len; + len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); } -static ssize_t ima_show_htable_violations(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, _htable.violations); -} - -static const struct file_operations ima_htable_violations_ops = { - .read = ima_show_htable_violations, - .llseek = generic_file_llseek, -}; - -static ssize_t ima_show_measurements_count(struct file *filp, - char __user *buf, - size_t count, loff_t *ppos) -{ - return ima_show_htable_value(buf, count, ppos, _htable.len); - -} - -static const struct file_operations ima_measurements_count_ops = { - .read = ima_show_measurements_count, +static const struct file_operations ima_htable_value_ops = { + .read = ima_show_htable_value, .llseek = generic_file_llseek, }; @@ -508,13 +494,13 @@ int __init ima_fs_init(void) runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, - _measurements_count_ops); + _htable_value_ops); if (IS_ERR(runtime_measurements_count)) goto out; violations = securityfs_create_file("violations", S_IRUSR | S_IRGRP, - ima_dir, NULL, _htable_violations_ops); + ima_dir, NULL, _htable_value_ops); if (IS_ERR(violations)) goto out; -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 03/12] ima: generalize policy file operations
This patch renames ima_open_policy() and ima_release_policy() respectively to ima_open_data_upload() and ima_release_data_upload(). They will be used to implement file operations for interfaces allowing to upload and read provided data. Also, the new flag IMA_POLICY_BUSY has been defined specifically for the policy, as it might not be cleared at file release. This would prevent userspace applications from uploading files after a policy has been loaded. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 46 - 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index e375206..f4199f2 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -384,6 +384,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, } enum ima_fs_flags { + IMA_POLICY_BUSY, IMA_FS_BUSY, }; @@ -399,22 +400,33 @@ static const struct seq_operations ima_policy_seqops = { #endif /* - * ima_open_policy: sequentialize access to the policy file + * ima_open_data_upload: sequentialize access to the data upload interface */ -static int ima_open_policy(struct inode *inode, struct file *filp) +static int ima_open_data_upload(struct inode *inode, struct file *filp) { + enum kernel_read_file_id file_id = ima_get_file_id(filp->f_path.dentry); + const struct seq_operations *seq_ops = NULL; + enum ima_fs_flags flag = IMA_FS_BUSY; + bool read_allowed = false; + + if (file_id == READING_POLICY) { + flag = IMA_POLICY_BUSY; +#ifdef CONFIG_IMA_READ_POLICY + read_allowed = true; + seq_ops = _policy_seqops; +#endif + } + if (!(filp->f_flags & O_WRONLY)) { -#ifndefCONFIG_IMA_READ_POLICY - return -EACCES; -#else + if (!read_allowed) + return -EACCES; if ((filp->f_flags & O_ACCMODE) != O_RDONLY) return -EACCES; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return seq_open(filp, _policy_seqops); -#endif + return seq_open(filp, seq_ops); } - if (test_and_set_bit(IMA_FS_BUSY, _fs_flags)) + if (test_and_set_bit(flag, _fs_flags)) return -EBUSY; return 0; } @@ -426,13 +438,19 @@ static int ima_open_policy(struct inode *inode, struct file *filp) * point to the new policy rules, and remove the securityfs policy file, * assuming a valid policy. */ -static int ima_release_policy(struct inode *inode, struct file *file) +static int ima_release_data_upload(struct inode *inode, struct file *file) { + enum kernel_read_file_id file_id = ima_get_file_id(file->f_path.dentry); const char *cause = valid_policy ? "completed" : "failed"; if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file); + if (file_id != READING_POLICY) { + clear_bit(IMA_FS_BUSY, _fs_flags); + return 0; + } + if (valid_policy && ima_check_policy() < 0) { cause = "failed"; valid_policy = 0; @@ -454,16 +472,16 @@ static int ima_release_policy(struct inode *inode, struct file *file) securityfs_remove(ima_policy); ima_policy = NULL; #else - clear_bit(IMA_FS_BUSY, _fs_flags); + clear_bit(IMA_POLICY_BUSY, _fs_flags); #endif return 0; } -static const struct file_operations ima_measure_policy_ops = { - .open = ima_open_policy, +static const struct file_operations ima_data_upload_ops = { + .open = ima_open_data_upload, .write = ima_write_data, .read = seq_read, - .release = ima_release_policy, + .release = ima_release_data_upload, .llseek = generic_file_llseek, }; @@ -502,7 +520,7 @@ int __init ima_fs_init(void) ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, - _measure_policy_ops); + _data_upload_ops); if (IS_ERR(ima_policy)) goto out; -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 02/12] ima: generalize ima_write_policy()
This patch renames ima_write_policy() to ima_write_data(). Also, it determines the kernel_read_file_id from the dentry associated to the file, and passes it to ima_read_file(). Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 55 ++--- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 058d3c1..e375206 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -28,6 +28,21 @@ static DEFINE_MUTEX(ima_write_mutex); +static struct dentry *ima_dir; +static struct dentry *binary_runtime_measurements; +static struct dentry *ascii_runtime_measurements; +static struct dentry *runtime_measurements_count; +static struct dentry *violations; +static struct dentry *ima_policy; + +static enum kernel_read_file_id ima_get_file_id(struct dentry *dentry) +{ + if (dentry == ima_policy) + return READING_POLICY; + + return READING_UNKNOWN; +} + bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) { @@ -315,11 +330,12 @@ static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) return pathlen; } -static ssize_t ima_write_policy(struct file *file, const char __user *buf, - size_t datalen, loff_t *ppos) +static ssize_t ima_write_data(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) { char *data; ssize_t result; + enum kernel_read_file_id file_id = ima_get_file_id(file->f_path.dentry); if (datalen >= PAGE_SIZE) datalen = PAGE_SIZE - 1; @@ -340,34 +356,33 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free; if (data[0] == '/') { - result = ima_read_file(data, READING_POLICY); - } else if (ima_appraise & IMA_APPRAISE_POLICY) { - pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); - integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, - "policy_update", "signed policy required", - 1, 0); - if (ima_appraise & IMA_APPRAISE_ENFORCE) - result = -EACCES; + result = ima_read_file(data, file_id); + } else if (file_id == READING_POLICY) { + if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("IMA: signed policy file (specified " + "as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", "signed policy required", + 1, 0); + if (ima_appraise & IMA_APPRAISE_ENFORCE) + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } } else { - result = ima_parse_add_rule(data); + pr_err("Unknown data type\n"); + result = -EINVAL; } mutex_unlock(_write_mutex); out_free: kfree(data); out: - if (result < 0) + if (file_id == READING_POLICY && result < 0) valid_policy = 0; return result; } -static struct dentry *ima_dir; -static struct dentry *binary_runtime_measurements; -static struct dentry *ascii_runtime_measurements; -static struct dentry *runtime_measurements_count; -static struct dentry *violations; -static struct dentry *ima_policy; - enum ima_fs_flags { IMA_FS_BUSY, }; @@ -446,7 +461,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, - .write = ima_write_policy, + .write = ima_write_data, .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek, -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/12] ima: generalize ima_read_policy()
Rename ima_read_policy() to ima_read_file(), and add file_id as new parameter. If file_id is equal to READING_POLICY, ima_read_file() behavior is the same of that without the patch. ima_read_file() will be used to read digest lists, to avoid reporting measurements when the file digest is known. Signed-off-by: Roberto Sassu <roberto.sa...@huawei.com> --- security/integrity/ima/ima_fs.c | 18 -- 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index ad491c5..058d3c1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -272,7 +272,7 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; -static ssize_t ima_read_policy(char *path) +static ssize_t ima_read_file(char *path, enum kernel_read_file_id file_id) { void *data; char *datap; @@ -285,16 +285,22 @@ static ssize_t ima_read_policy(char *path) datap = path; strsep(, "\n"); - rc = kernel_read_file_from_path(path, , , 0, READING_POLICY); + rc = kernel_read_file_from_path(path, , , 0, file_id); if (rc < 0) { pr_err("Unable to open file: %s (%d)", path, rc); return rc; } datap = data; - while (size > 0 && (p = strsep(, "\n"))) { - pr_debug("rule: %s\n", p); - rc = ima_parse_add_rule(p); + while (size > 0) { + if (file_id == READING_POLICY) { + p = strsep(, "\n"); + if (p == NULL) + break; + + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + } if (rc < 0) break; size -= rc; @@ -334,7 +340,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, goto out_free; if (data[0] == '/') { - result = ima_read_policy(data); + result = ima_read_file(data, READING_POLICY); } else if (ima_appraise & IMA_APPRAISE_POLICY) { pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html