diff options
-rw-r--r-- | Documentation/ABI/testing/evm | 23 | ||||
-rw-r--r-- | include/linux/integrity.h | 7 | ||||
-rw-r--r-- | include/linux/xattr.h | 3 | ||||
-rw-r--r-- | security/integrity/Kconfig | 3 | ||||
-rw-r--r-- | security/integrity/Makefile | 2 | ||||
-rw-r--r-- | security/integrity/evm/Kconfig | 12 | ||||
-rw-r--r-- | security/integrity/evm/Makefile | 6 | ||||
-rw-r--r-- | security/integrity/evm/evm.h | 33 | ||||
-rw-r--r-- | security/integrity/evm/evm_crypto.c | 183 | ||||
-rw-r--r-- | security/integrity/evm/evm_main.c | 284 | ||||
-rw-r--r-- | security/integrity/evm/evm_secfs.c | 108 | ||||
-rw-r--r-- | security/integrity/iint.c | 1 | ||||
-rw-r--r-- | security/integrity/integrity.h | 1 |
13 files changed, 665 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/evm b/Documentation/ABI/testing/evm new file mode 100644 index 000000000000..8374d4557e5d --- /dev/null +++ b/Documentation/ABI/testing/evm | |||
@@ -0,0 +1,23 @@ | |||
1 | What: security/evm | ||
2 | Date: March 2011 | ||
3 | Contact: Mimi Zohar <zohar@us.ibm.com> | ||
4 | Description: | ||
5 | EVM protects a file's security extended attributes(xattrs) | ||
6 | against integrity attacks. The initial method maintains an | ||
7 | HMAC-sha1 value across the extended attributes, storing the | ||
8 | value as the extended attribute 'security.evm'. | ||
9 | |||
10 | EVM depends on the Kernel Key Retention System to provide it | ||
11 | with a trusted/encrypted key for the HMAC-sha1 operation. | ||
12 | The key is loaded onto the root's keyring using keyctl. Until | ||
13 | EVM receives notification that the key has been successfully | ||
14 | loaded onto the keyring (echo 1 > <securityfs>/evm), EVM | ||
15 | can not create or validate the 'security.evm' xattr, but | ||
16 | returns INTEGRITY_UNKNOWN. Loading the key and signaling EVM | ||
17 | should be done as early as possible. Normally this is done | ||
18 | in the initramfs, which has already been measured as part | ||
19 | of the trusted boot. For more information on creating and | ||
20 | loading existing trusted/encrypted keys, refer to: | ||
21 | Documentation/keys-trusted-encrypted.txt. (A sample dracut | ||
22 | patch, which loads the trusted/encrypted key and enables | ||
23 | EVM, is available from http://linux-ima.sourceforge.net/#EVM.) | ||
diff --git a/include/linux/integrity.h b/include/linux/integrity.h index 905981247327..e715a2abcea2 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h | |||
@@ -12,6 +12,13 @@ | |||
12 | 12 | ||
13 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
14 | 14 | ||
15 | enum integrity_status { | ||
16 | INTEGRITY_PASS = 0, | ||
17 | INTEGRITY_FAIL, | ||
18 | INTEGRITY_NOLABEL, | ||
19 | INTEGRITY_UNKNOWN, | ||
20 | }; | ||
21 | |||
15 | #ifdef CONFIG_INTEGRITY | 22 | #ifdef CONFIG_INTEGRITY |
16 | extern int integrity_inode_alloc(struct inode *inode); | 23 | extern int integrity_inode_alloc(struct inode *inode); |
17 | extern void integrity_inode_free(struct inode *inode); | 24 | extern void integrity_inode_free(struct inode *inode); |
diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 4703f6bd1f53..b20cb965c322 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h | |||
@@ -30,6 +30,9 @@ | |||
30 | #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) | 30 | #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) |
31 | 31 | ||
32 | /* Security namespace */ | 32 | /* Security namespace */ |
33 | #define XATTR_EVM_SUFFIX "evm" | ||
34 | #define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX | ||
35 | |||
33 | #define XATTR_SELINUX_SUFFIX "selinux" | 36 | #define XATTR_SELINUX_SUFFIX "selinux" |
34 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX | 37 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX |
35 | 38 | ||
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 270469155681..4bf00acf7937 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | # | 1 | # |
2 | config INTEGRITY | 2 | config INTEGRITY |
3 | def_bool y | 3 | def_bool y |
4 | depends on IMA | 4 | depends on IMA || EVM |
5 | 5 | ||
6 | source security/integrity/ima/Kconfig | 6 | source security/integrity/ima/Kconfig |
7 | source security/integrity/evm/Kconfig | ||
diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 6eddd61b84e8..0ae44aea6516 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile | |||
@@ -8,3 +8,5 @@ integrity-y := iint.o | |||
8 | 8 | ||
9 | subdir-$(CONFIG_IMA) += ima | 9 | subdir-$(CONFIG_IMA) += ima |
10 | obj-$(CONFIG_IMA) += ima/built-in.o | 10 | obj-$(CONFIG_IMA) += ima/built-in.o |
11 | subdir-$(CONFIG_EVM) += evm | ||
12 | obj-$(CONFIG_EVM) += evm/built-in.o | ||
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig new file mode 100644 index 000000000000..73f654099a4b --- /dev/null +++ b/security/integrity/evm/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | config EVM | ||
2 | boolean "EVM support" | ||
3 | depends on SECURITY && KEYS && ENCRYPTED_KEYS | ||
4 | select CRYPTO_HMAC | ||
5 | select CRYPTO_MD5 | ||
6 | select CRYPTO_SHA1 | ||
7 | default n | ||
8 | help | ||
9 | EVM protects a file's security extended attributes against | ||
10 | integrity attacks. | ||
11 | |||
12 | If you are unsure how to answer this question, answer N. | ||
diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile new file mode 100644 index 000000000000..0787d262b9e3 --- /dev/null +++ b/security/integrity/evm/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for building the Extended Verification Module(EVM) | ||
3 | # | ||
4 | obj-$(CONFIG_EVM) += evm.o | ||
5 | |||
6 | evm-y := evm_main.o evm_crypto.o evm_secfs.o | ||
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h new file mode 100644 index 000000000000..375dc3e6015c --- /dev/null +++ b/security/integrity/evm/evm.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2010 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Mimi Zohar <zohar@us.ibm.com> | ||
6 | * Kylene Hall <kjhall@us.ibm.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, version 2 of the License. | ||
11 | * | ||
12 | * File: evm.h | ||
13 | * | ||
14 | */ | ||
15 | #include <linux/security.h> | ||
16 | #include "../integrity.h" | ||
17 | |||
18 | extern int evm_initialized; | ||
19 | extern char *evm_hmac; | ||
20 | |||
21 | /* List of EVM protected security xattrs */ | ||
22 | extern char *evm_config_xattrnames[]; | ||
23 | |||
24 | extern int evm_init_key(void); | ||
25 | extern int evm_update_evmxattr(struct dentry *dentry, | ||
26 | const char *req_xattr_name, | ||
27 | const char *req_xattr_value, | ||
28 | size_t req_xattr_value_len); | ||
29 | extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, | ||
30 | const char *req_xattr_value, | ||
31 | size_t req_xattr_value_len, char *digest); | ||
32 | extern int evm_init_secfs(void); | ||
33 | extern void evm_cleanup_secfs(void); | ||
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c new file mode 100644 index 000000000000..d49bb002f3da --- /dev/null +++ b/security/integrity/evm/evm_crypto.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2010 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Mimi Zohar <zohar@us.ibm.com> | ||
6 | * Kylene Hall <kjhall@us.ibm.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, version 2 of the License. | ||
11 | * | ||
12 | * File: evm_crypto.c | ||
13 | * Using root's kernel master key (kmk), calculate the HMAC | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/crypto.h> | ||
18 | #include <linux/xattr.h> | ||
19 | #include <linux/scatterlist.h> | ||
20 | #include <keys/encrypted-type.h> | ||
21 | #include "evm.h" | ||
22 | |||
23 | #define EVMKEY "evm-key" | ||
24 | #define MAX_KEY_SIZE 128 | ||
25 | static unsigned char evmkey[MAX_KEY_SIZE]; | ||
26 | static int evmkey_len = MAX_KEY_SIZE; | ||
27 | |||
28 | static int init_desc(struct hash_desc *desc) | ||
29 | { | ||
30 | int rc; | ||
31 | |||
32 | desc->tfm = crypto_alloc_hash(evm_hmac, 0, CRYPTO_ALG_ASYNC); | ||
33 | if (IS_ERR(desc->tfm)) { | ||
34 | pr_info("Can not allocate %s (reason: %ld)\n", | ||
35 | evm_hmac, PTR_ERR(desc->tfm)); | ||
36 | rc = PTR_ERR(desc->tfm); | ||
37 | return rc; | ||
38 | } | ||
39 | desc->flags = 0; | ||
40 | rc = crypto_hash_setkey(desc->tfm, evmkey, evmkey_len); | ||
41 | if (rc) | ||
42 | goto out; | ||
43 | rc = crypto_hash_init(desc); | ||
44 | out: | ||
45 | if (rc) | ||
46 | crypto_free_hash(desc->tfm); | ||
47 | return rc; | ||
48 | } | ||
49 | |||
50 | /* Protect against 'cutting & pasting' security.evm xattr, include inode | ||
51 | * specific info. | ||
52 | * | ||
53 | * (Additional directory/file metadata needs to be added for more complete | ||
54 | * protection.) | ||
55 | */ | ||
56 | static void hmac_add_misc(struct hash_desc *desc, struct inode *inode, | ||
57 | char *digest) | ||
58 | { | ||
59 | struct h_misc { | ||
60 | unsigned long ino; | ||
61 | __u32 generation; | ||
62 | uid_t uid; | ||
63 | gid_t gid; | ||
64 | umode_t mode; | ||
65 | } hmac_misc; | ||
66 | struct scatterlist sg[1]; | ||
67 | |||
68 | memset(&hmac_misc, 0, sizeof hmac_misc); | ||
69 | hmac_misc.ino = inode->i_ino; | ||
70 | hmac_misc.generation = inode->i_generation; | ||
71 | hmac_misc.uid = inode->i_uid; | ||
72 | hmac_misc.gid = inode->i_gid; | ||
73 | hmac_misc.mode = inode->i_mode; | ||
74 | sg_init_one(sg, &hmac_misc, sizeof hmac_misc); | ||
75 | crypto_hash_update(desc, sg, sizeof hmac_misc); | ||
76 | crypto_hash_final(desc, digest); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Calculate the HMAC value across the set of protected security xattrs. | ||
81 | * | ||
82 | * Instead of retrieving the requested xattr, for performance, calculate | ||
83 | * the hmac using the requested xattr value. Don't alloc/free memory for | ||
84 | * each xattr, but attempt to re-use the previously allocated memory. | ||
85 | */ | ||
86 | int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, | ||
87 | const char *req_xattr_value, size_t req_xattr_value_len, | ||
88 | char *digest) | ||
89 | { | ||
90 | struct inode *inode = dentry->d_inode; | ||
91 | struct hash_desc desc; | ||
92 | struct scatterlist sg[1]; | ||
93 | char **xattrname; | ||
94 | size_t xattr_size = 0; | ||
95 | char *xattr_value = NULL; | ||
96 | int error; | ||
97 | int size; | ||
98 | |||
99 | if (!inode->i_op || !inode->i_op->getxattr) | ||
100 | return -EOPNOTSUPP; | ||
101 | error = init_desc(&desc); | ||
102 | if (error) | ||
103 | return error; | ||
104 | |||
105 | error = -ENODATA; | ||
106 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { | ||
107 | if ((req_xattr_name && req_xattr_value) | ||
108 | && !strcmp(*xattrname, req_xattr_name)) { | ||
109 | error = 0; | ||
110 | sg_init_one(sg, req_xattr_value, req_xattr_value_len); | ||
111 | crypto_hash_update(&desc, sg, req_xattr_value_len); | ||
112 | continue; | ||
113 | } | ||
114 | size = vfs_getxattr_alloc(dentry, *xattrname, | ||
115 | &xattr_value, xattr_size, GFP_NOFS); | ||
116 | if (size == -ENOMEM) { | ||
117 | error = -ENOMEM; | ||
118 | goto out; | ||
119 | } | ||
120 | if (size < 0) | ||
121 | continue; | ||
122 | |||
123 | error = 0; | ||
124 | xattr_size = size; | ||
125 | sg_init_one(sg, xattr_value, xattr_size); | ||
126 | crypto_hash_update(&desc, sg, xattr_size); | ||
127 | } | ||
128 | hmac_add_misc(&desc, inode, digest); | ||
129 | kfree(xattr_value); | ||
130 | out: | ||
131 | crypto_free_hash(desc.tfm); | ||
132 | return error; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Calculate the hmac and update security.evm xattr | ||
137 | * | ||
138 | * Expects to be called with i_mutex locked. | ||
139 | */ | ||
140 | int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, | ||
141 | const char *xattr_value, size_t xattr_value_len) | ||
142 | { | ||
143 | struct inode *inode = dentry->d_inode; | ||
144 | u8 hmac[SHA1_DIGEST_SIZE]; | ||
145 | int rc = 0; | ||
146 | |||
147 | rc = evm_calc_hmac(dentry, xattr_name, xattr_value, | ||
148 | xattr_value_len, hmac); | ||
149 | if (rc == 0) | ||
150 | rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, | ||
151 | hmac, SHA1_DIGEST_SIZE, 0); | ||
152 | else if (rc == -ENODATA) | ||
153 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM); | ||
154 | return rc; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Get the key from the TPM for the SHA1-HMAC | ||
159 | */ | ||
160 | int evm_init_key(void) | ||
161 | { | ||
162 | struct key *evm_key; | ||
163 | struct encrypted_key_payload *ekp; | ||
164 | int rc = 0; | ||
165 | |||
166 | evm_key = request_key(&key_type_encrypted, EVMKEY, NULL); | ||
167 | if (IS_ERR(evm_key)) | ||
168 | return -ENOENT; | ||
169 | |||
170 | down_read(&evm_key->sem); | ||
171 | ekp = evm_key->payload.data; | ||
172 | if (ekp->decrypted_datalen > MAX_KEY_SIZE) { | ||
173 | rc = -EINVAL; | ||
174 | goto out; | ||
175 | } | ||
176 | memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen); | ||
177 | out: | ||
178 | /* burn the original key contents */ | ||
179 | memset(ekp->decrypted_data, 0, ekp->decrypted_datalen); | ||
180 | up_read(&evm_key->sem); | ||
181 | key_put(evm_key); | ||
182 | return rc; | ||
183 | } | ||
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c new file mode 100644 index 000000000000..a8fa45fef8f1 --- /dev/null +++ b/security/integrity/evm/evm_main.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2010 IBM Corporation | ||
3 | * | ||
4 | * Author: | ||
5 | * Mimi Zohar <zohar@us.ibm.com> | ||
6 | * Kylene Hall <kjhall@us.ibm.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, version 2 of the License. | ||
11 | * | ||
12 | * File: evm_main.c | ||
13 | * implements evm_inode_setxattr, evm_inode_post_setxattr, | ||
14 | * evm_inode_removexattr, and evm_verifyxattr | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/crypto.h> | ||
19 | #include <linux/xattr.h> | ||
20 | #include <linux/integrity.h> | ||
21 | #include "evm.h" | ||
22 | |||
23 | int evm_initialized; | ||
24 | |||
25 | char *evm_hmac = "hmac(sha1)"; | ||
26 | |||
27 | char *evm_config_xattrnames[] = { | ||
28 | #ifdef CONFIG_SECURITY_SELINUX | ||
29 | XATTR_NAME_SELINUX, | ||
30 | #endif | ||
31 | #ifdef CONFIG_SECURITY_SMACK | ||
32 | XATTR_NAME_SMACK, | ||
33 | #endif | ||
34 | XATTR_NAME_CAPS, | ||
35 | NULL | ||
36 | }; | ||
37 | |||
38 | /* | ||
39 | * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr | ||
40 | * | ||
41 | * Compute the HMAC on the dentry's protected set of extended attributes | ||
42 | * and compare it against the stored security.evm xattr. (For performance, | ||
43 | * use the previoulsy retrieved xattr value and length to calculate the | ||
44 | * HMAC.) | ||
45 | * | ||
46 | * Returns integrity status | ||
47 | */ | ||
48 | static enum integrity_status evm_verify_hmac(struct dentry *dentry, | ||
49 | const char *xattr_name, | ||
50 | char *xattr_value, | ||
51 | size_t xattr_value_len, | ||
52 | struct integrity_iint_cache *iint) | ||
53 | { | ||
54 | char hmac_val[SHA1_DIGEST_SIZE]; | ||
55 | int rc; | ||
56 | |||
57 | if (iint->hmac_status != INTEGRITY_UNKNOWN) | ||
58 | return iint->hmac_status; | ||
59 | |||
60 | memset(hmac_val, 0, sizeof hmac_val); | ||
61 | rc = evm_calc_hmac(dentry, xattr_name, xattr_value, | ||
62 | xattr_value_len, hmac_val); | ||
63 | if (rc < 0) | ||
64 | return INTEGRITY_UNKNOWN; | ||
65 | |||
66 | rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, hmac_val, sizeof hmac_val, | ||
67 | GFP_NOFS); | ||
68 | if (rc < 0) | ||
69 | goto err_out; | ||
70 | iint->hmac_status = INTEGRITY_PASS; | ||
71 | return iint->hmac_status; | ||
72 | |||
73 | err_out: | ||
74 | switch (rc) { | ||
75 | case -ENODATA: /* file not labelled */ | ||
76 | iint->hmac_status = INTEGRITY_NOLABEL; | ||
77 | break; | ||
78 | case -EINVAL: | ||
79 | iint->hmac_status = INTEGRITY_FAIL; | ||
80 | break; | ||
81 | default: | ||
82 | iint->hmac_status = INTEGRITY_UNKNOWN; | ||
83 | } | ||
84 | return iint->hmac_status; | ||
85 | } | ||
86 | |||
87 | static int evm_protected_xattr(const char *req_xattr_name) | ||
88 | { | ||
89 | char **xattrname; | ||
90 | int namelen; | ||
91 | int found = 0; | ||
92 | |||
93 | namelen = strlen(req_xattr_name); | ||
94 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { | ||
95 | if ((strlen(*xattrname) == namelen) | ||
96 | && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) { | ||
97 | found = 1; | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | return found; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * evm_verifyxattr - verify the integrity of the requested xattr | ||
106 | * @dentry: object of the verify xattr | ||
107 | * @xattr_name: requested xattr | ||
108 | * @xattr_value: requested xattr value | ||
109 | * @xattr_value_len: requested xattr value length | ||
110 | * | ||
111 | * Calculate the HMAC for the given dentry and verify it against the stored | ||
112 | * security.evm xattr. For performance, use the xattr value and length | ||
113 | * previously retrieved to calculate the HMAC. | ||
114 | * | ||
115 | * Returns the xattr integrity status. | ||
116 | * | ||
117 | * This function requires the caller to lock the inode's i_mutex before it | ||
118 | * is executed. | ||
119 | */ | ||
120 | enum integrity_status evm_verifyxattr(struct dentry *dentry, | ||
121 | const char *xattr_name, | ||
122 | void *xattr_value, size_t xattr_value_len) | ||
123 | { | ||
124 | struct inode *inode = dentry->d_inode; | ||
125 | struct integrity_iint_cache *iint; | ||
126 | enum integrity_status status; | ||
127 | |||
128 | if (!evm_initialized || !evm_protected_xattr(xattr_name)) | ||
129 | return INTEGRITY_UNKNOWN; | ||
130 | |||
131 | iint = integrity_iint_find(inode); | ||
132 | if (!iint) | ||
133 | return INTEGRITY_UNKNOWN; | ||
134 | status = evm_verify_hmac(dentry, xattr_name, xattr_value, | ||
135 | xattr_value_len, iint); | ||
136 | return status; | ||
137 | } | ||
138 | EXPORT_SYMBOL_GPL(evm_verifyxattr); | ||
139 | |||
140 | /* | ||
141 | * evm_protect_xattr - protect the EVM extended attribute | ||
142 | * | ||
143 | * Prevent security.evm from being modified or removed. | ||
144 | */ | ||
145 | static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, | ||
146 | const void *xattr_value, size_t xattr_value_len) | ||
147 | { | ||
148 | if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) { | ||
149 | if (!capable(CAP_SYS_ADMIN)) | ||
150 | return -EPERM; | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * evm_inode_setxattr - protect the EVM extended attribute | ||
157 | * @dentry: pointer to the affected dentry | ||
158 | * @xattr_name: pointer to the affected extended attribute name | ||
159 | * @xattr_value: pointer to the new extended attribute value | ||
160 | * @xattr_value_len: pointer to the new extended attribute value length | ||
161 | * | ||
162 | * Prevent 'security.evm' from being modified | ||
163 | */ | ||
164 | int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, | ||
165 | const void *xattr_value, size_t xattr_value_len) | ||
166 | { | ||
167 | return evm_protect_xattr(dentry, xattr_name, xattr_value, | ||
168 | xattr_value_len); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * evm_inode_removexattr - protect the EVM extended attribute | ||
173 | * @dentry: pointer to the affected dentry | ||
174 | * @xattr_name: pointer to the affected extended attribute name | ||
175 | * | ||
176 | * Prevent 'security.evm' from being removed. | ||
177 | */ | ||
178 | int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name) | ||
179 | { | ||
180 | return evm_protect_xattr(dentry, xattr_name, NULL, 0); | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * evm_inode_post_setxattr - update 'security.evm' to reflect the changes | ||
185 | * @dentry: pointer to the affected dentry | ||
186 | * @xattr_name: pointer to the affected extended attribute name | ||
187 | * @xattr_value: pointer to the new extended attribute value | ||
188 | * @xattr_value_len: pointer to the new extended attribute value length | ||
189 | * | ||
190 | * Update the HMAC stored in 'security.evm' to reflect the change. | ||
191 | * | ||
192 | * No need to take the i_mutex lock here, as this function is called from | ||
193 | * __vfs_setxattr_noperm(). The caller of which has taken the inode's | ||
194 | * i_mutex lock. | ||
195 | */ | ||
196 | void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, | ||
197 | const void *xattr_value, size_t xattr_value_len) | ||
198 | { | ||
199 | if (!evm_initialized || !evm_protected_xattr(xattr_name)) | ||
200 | return; | ||
201 | |||
202 | evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); | ||
203 | return; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * evm_inode_post_removexattr - update 'security.evm' after removing the xattr | ||
208 | * @dentry: pointer to the affected dentry | ||
209 | * @xattr_name: pointer to the affected extended attribute name | ||
210 | * | ||
211 | * Update the HMAC stored in 'security.evm' to reflect removal of the xattr. | ||
212 | */ | ||
213 | void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) | ||
214 | { | ||
215 | struct inode *inode = dentry->d_inode; | ||
216 | |||
217 | if (!evm_initialized || !evm_protected_xattr(xattr_name)) | ||
218 | return; | ||
219 | |||
220 | mutex_lock(&inode->i_mutex); | ||
221 | evm_update_evmxattr(dentry, xattr_name, NULL, 0); | ||
222 | mutex_unlock(&inode->i_mutex); | ||
223 | return; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * evm_inode_post_setattr - update 'security.evm' after modifying metadata | ||
228 | * @dentry: pointer to the affected dentry | ||
229 | * @ia_valid: for the UID and GID status | ||
230 | * | ||
231 | * For now, update the HMAC stored in 'security.evm' to reflect UID/GID | ||
232 | * changes. | ||
233 | * | ||
234 | * This function is called from notify_change(), which expects the caller | ||
235 | * to lock the inode's i_mutex. | ||
236 | */ | ||
237 | void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) | ||
238 | { | ||
239 | if (!evm_initialized) | ||
240 | return; | ||
241 | |||
242 | if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) | ||
243 | evm_update_evmxattr(dentry, NULL, NULL, 0); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | static struct crypto_hash *tfm_hmac; /* preload crypto alg */ | ||
248 | static int __init init_evm(void) | ||
249 | { | ||
250 | int error; | ||
251 | |||
252 | tfm_hmac = crypto_alloc_hash(evm_hmac, 0, CRYPTO_ALG_ASYNC); | ||
253 | error = evm_init_secfs(); | ||
254 | if (error < 0) { | ||
255 | printk(KERN_INFO "EVM: Error registering secfs\n"); | ||
256 | goto err; | ||
257 | } | ||
258 | err: | ||
259 | return error; | ||
260 | } | ||
261 | |||
262 | static void __exit cleanup_evm(void) | ||
263 | { | ||
264 | evm_cleanup_secfs(); | ||
265 | crypto_free_hash(tfm_hmac); | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * evm_display_config - list the EVM protected security extended attributes | ||
270 | */ | ||
271 | static int __init evm_display_config(void) | ||
272 | { | ||
273 | char **xattrname; | ||
274 | |||
275 | for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) | ||
276 | printk(KERN_INFO "EVM: %s\n", *xattrname); | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | pure_initcall(evm_display_config); | ||
281 | late_initcall(init_evm); | ||
282 | |||
283 | MODULE_DESCRIPTION("Extended Verification Module"); | ||
284 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c new file mode 100644 index 000000000000..ac7629950578 --- /dev/null +++ b/security/integrity/evm/evm_secfs.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Mimi Zohar <zohar@us.ibm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, version 2 of the License. | ||
10 | * | ||
11 | * File: evm_secfs.c | ||
12 | * - Used to signal when key is on keyring | ||
13 | * - Get the key and enable EVM | ||
14 | */ | ||
15 | |||
16 | #include <linux/uaccess.h> | ||
17 | #include <linux/module.h> | ||
18 | #include "evm.h" | ||
19 | |||
20 | static struct dentry *evm_init_tpm; | ||
21 | |||
22 | /** | ||
23 | * evm_read_key - read() for <securityfs>/evm | ||
24 | * | ||
25 | * @filp: file pointer, not actually used | ||
26 | * @buf: where to put the result | ||
27 | * @count: maximum to send along | ||
28 | * @ppos: where to start | ||
29 | * | ||
30 | * Returns number of bytes read or error code, as appropriate | ||
31 | */ | ||
32 | static ssize_t evm_read_key(struct file *filp, char __user *buf, | ||
33 | size_t count, loff_t *ppos) | ||
34 | { | ||
35 | char temp[80]; | ||
36 | ssize_t rc; | ||
37 | |||
38 | if (*ppos != 0) | ||
39 | return 0; | ||
40 | |||
41 | sprintf(temp, "%d", evm_initialized); | ||
42 | rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); | ||
43 | |||
44 | return rc; | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * evm_write_key - write() for <securityfs>/evm | ||
49 | * @file: file pointer, not actually used | ||
50 | * @buf: where to get the data from | ||
51 | * @count: bytes sent | ||
52 | * @ppos: where to start | ||
53 | * | ||
54 | * Used to signal that key is on the kernel key ring. | ||
55 | * - get the integrity hmac key from the kernel key ring | ||
56 | * - create list of hmac protected extended attributes | ||
57 | * Returns number of bytes written or error code, as appropriate | ||
58 | */ | ||
59 | static ssize_t evm_write_key(struct file *file, const char __user *buf, | ||
60 | size_t count, loff_t *ppos) | ||
61 | { | ||
62 | char temp[80]; | ||
63 | int i, error; | ||
64 | |||
65 | if (!capable(CAP_SYS_ADMIN) || evm_initialized) | ||
66 | return -EPERM; | ||
67 | |||
68 | if (count >= sizeof(temp) || count == 0) | ||
69 | return -EINVAL; | ||
70 | |||
71 | if (copy_from_user(temp, buf, count) != 0) | ||
72 | return -EFAULT; | ||
73 | |||
74 | temp[count] = '\0'; | ||
75 | |||
76 | if ((sscanf(temp, "%d", &i) != 1) || (i != 1)) | ||
77 | return -EINVAL; | ||
78 | |||
79 | error = evm_init_key(); | ||
80 | if (!error) { | ||
81 | evm_initialized = 1; | ||
82 | pr_info("EVM: initialized\n"); | ||
83 | } else | ||
84 | pr_err("EVM: initialization failed\n"); | ||
85 | return count; | ||
86 | } | ||
87 | |||
88 | static const struct file_operations evm_key_ops = { | ||
89 | .read = evm_read_key, | ||
90 | .write = evm_write_key, | ||
91 | }; | ||
92 | |||
93 | int __init evm_init_secfs(void) | ||
94 | { | ||
95 | int error = 0; | ||
96 | |||
97 | evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP, | ||
98 | NULL, NULL, &evm_key_ops); | ||
99 | if (!evm_init_tpm || IS_ERR(evm_init_tpm)) | ||
100 | error = -EFAULT; | ||
101 | return error; | ||
102 | } | ||
103 | |||
104 | void __exit evm_cleanup_secfs(void) | ||
105 | { | ||
106 | if (evm_init_tpm) | ||
107 | securityfs_remove(evm_init_tpm); | ||
108 | } | ||
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index d17de48bd6cc..991df20709b0 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c | |||
@@ -157,6 +157,7 @@ static void init_once(void *foo) | |||
157 | iint->version = 0; | 157 | iint->version = 0; |
158 | iint->flags = 0UL; | 158 | iint->flags = 0UL; |
159 | mutex_init(&iint->mutex); | 159 | mutex_init(&iint->mutex); |
160 | iint->hmac_status = INTEGRITY_UNKNOWN; | ||
160 | } | 161 | } |
161 | 162 | ||
162 | static int __init integrity_iintcache_init(void) | 163 | static int __init integrity_iintcache_init(void) |
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7351836325a8..397a46b3992f 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h | |||
@@ -26,6 +26,7 @@ struct integrity_iint_cache { | |||
26 | unsigned char flags; | 26 | unsigned char flags; |
27 | u8 digest[SHA1_DIGEST_SIZE]; | 27 | u8 digest[SHA1_DIGEST_SIZE]; |
28 | struct mutex mutex; /* protects: version, flags, digest */ | 28 | struct mutex mutex; /* protects: version, flags, digest */ |
29 | enum integrity_status hmac_status; | ||
29 | }; | 30 | }; |
30 | 31 | ||
31 | /* rbtree tree calls to lookup, insert, delete | 32 | /* rbtree tree calls to lookup, insert, delete |