summaryrefslogtreecommitdiff
path: root/security/integrity/ima/ima_crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima/ima_crypto.c')
-rw-r--r--security/integrity/ima/ima_crypto.c98
1 files changed, 96 insertions, 2 deletions
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index b9e5120559d4..84b938f86bea 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -37,6 +37,33 @@ static unsigned long ima_ahash_minsize;
module_param_named(ahash_minsize, ima_ahash_minsize, ulong, 0644);
MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use");
+/* default is 0 - 1 page. */
+static int ima_maxorder;
+static unsigned int ima_bufsize = PAGE_SIZE;
+
+static int param_set_bufsize(const char *val, const struct kernel_param *kp)
+{
+ unsigned long long size;
+ int order;
+
+ size = memparse(val, NULL);
+ order = get_order(size);
+ if (order >= MAX_ORDER)
+ return -EINVAL;
+ ima_maxorder = order;
+ ima_bufsize = PAGE_SIZE << order;
+ return 0;
+}
+
+static struct kernel_param_ops param_ops_bufsize = {
+ .set = param_set_bufsize,
+ .get = param_get_uint,
+};
+#define param_check_bufsize(name, p) __param_check(name, p, unsigned int)
+
+module_param_named(ahash_bufsize, ima_bufsize, bufsize, 0644);
+MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
+
static struct crypto_shash *ima_shash_tfm;
static struct crypto_ahash *ima_ahash_tfm;
@@ -106,6 +133,68 @@ static void ima_free_tfm(struct crypto_shash *tfm)
crypto_free_shash(tfm);
}
+/**
+ * ima_alloc_pages() - Allocate contiguous pages.
+ * @max_size: Maximum amount of memory to allocate.
+ * @allocated_size: Returned size of actual allocation.
+ * @last_warn: Should the min_size allocation warn or not.
+ *
+ * Tries to do opportunistic allocation for memory first trying to allocate
+ * max_size amount of memory and then splitting that until zero order is
+ * reached. Allocation is tried without generating allocation warnings unless
+ * last_warn is set. Last_warn set affects only last allocation of zero order.
+ *
+ * By default, ima_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL)
+ *
+ * Return pointer to allocated memory, or NULL on failure.
+ */
+static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
+ int last_warn)
+{
+ void *ptr;
+ int order = ima_maxorder;
+ gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY;
+
+ if (order)
+ order = min(get_order(max_size), order);
+
+ for (; order; order--) {
+ ptr = (void *)__get_free_pages(gfp_mask, order);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE << order;
+ return ptr;
+ }
+ }
+
+ /* order is zero - one page */
+
+ gfp_mask = GFP_KERNEL;
+
+ if (!last_warn)
+ gfp_mask |= __GFP_NOWARN;
+
+ ptr = (void *)__get_free_pages(gfp_mask, 0);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE;
+ return ptr;
+ }
+
+ *allocated_size = 0;
+ return NULL;
+}
+
+/**
+ * ima_free_pages() - Free pages allocated by ima_alloc_pages().
+ * @ptr: Pointer to allocated pages.
+ * @size: Size of allocated buffer.
+ */
+static void ima_free_pages(void *ptr, size_t size)
+{
+ if (!ptr)
+ return;
+ free_pages((unsigned long)ptr, get_order(size));
+}
+
static struct crypto_ahash *ima_alloc_atfm(enum hash_algo algo)
{
struct crypto_ahash *tfm = ima_ahash_tfm;
@@ -169,6 +258,7 @@ static int ima_calc_file_hash_atfm(struct file *file,
struct ahash_request *req;
struct scatterlist sg[1];
struct ahash_completion res;
+ size_t rbuf_size;
hash->length = crypto_ahash_digestsize(tfm);
@@ -190,7 +280,11 @@ static int ima_calc_file_hash_atfm(struct file *file,
if (i_size == 0)
goto out2;
- rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ /*
+ * Try to allocate maximum size of memory.
+ * Fail if even a single page cannot be allocated.
+ */
+ rbuf = ima_alloc_pages(i_size, &rbuf_size, 1);
if (!rbuf) {
rc = -ENOMEM;
goto out1;
@@ -219,7 +313,7 @@ static int ima_calc_file_hash_atfm(struct file *file,
}
if (read)
file->f_mode &= ~FMODE_READ;
- kfree(rbuf);
+ ima_free_pages(rbuf, rbuf_size);
out2:
if (!rc) {
ahash_request_set_crypt(req, NULL, hash->digest, 0);