1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4   */
5  
6  #include <linux/fs.h>
7  #include <linux/fs_struct.h>
8  #include <linux/types.h>
9  #include <linux/binfmts.h>
10  #include <linux/mman.h>
11  #include <linux/blk_types.h>
12  
13  #include "ipe.h"
14  #include "hooks.h"
15  #include "eval.h"
16  #include "digest.h"
17  
18  /**
19   * ipe_bprm_check_security() - ipe security hook function for bprm check.
20   * @bprm: Supplies a pointer to a linux_binprm structure to source the file
21   *	  being evaluated.
22   *
23   * This LSM hook is called when a binary is loaded through the exec
24   * family of system calls.
25   *
26   * Return:
27   * * %0		- Success
28   * * %-EACCES	- Did not pass IPE policy
29   */
ipe_bprm_check_security(struct linux_binprm * bprm)30  int ipe_bprm_check_security(struct linux_binprm *bprm)
31  {
32  	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
33  
34  	ipe_build_eval_ctx(&ctx, bprm->file, IPE_OP_EXEC, IPE_HOOK_BPRM_CHECK);
35  	return ipe_evaluate_event(&ctx);
36  }
37  
38  /**
39   * ipe_mmap_file() - ipe security hook function for mmap check.
40   * @f: File being mmap'd. Can be NULL in the case of anonymous memory.
41   * @reqprot: The requested protection on the mmap, passed from usermode.
42   * @prot: The effective protection on the mmap, resolved from reqprot and
43   *	  system configuration.
44   * @flags: Unused.
45   *
46   * This hook is called when a file is loaded through the mmap
47   * family of system calls.
48   *
49   * Return:
50   * * %0		- Success
51   * * %-EACCES	- Did not pass IPE policy
52   */
ipe_mmap_file(struct file * f,unsigned long reqprot __always_unused,unsigned long prot,unsigned long flags)53  int ipe_mmap_file(struct file *f, unsigned long reqprot __always_unused,
54  		  unsigned long prot, unsigned long flags)
55  {
56  	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
57  
58  	if (prot & PROT_EXEC) {
59  		ipe_build_eval_ctx(&ctx, f, IPE_OP_EXEC, IPE_HOOK_MMAP);
60  		return ipe_evaluate_event(&ctx);
61  	}
62  
63  	return 0;
64  }
65  
66  /**
67   * ipe_file_mprotect() - ipe security hook function for mprotect check.
68   * @vma: Existing virtual memory area created by mmap or similar.
69   * @reqprot: The requested protection on the mmap, passed from usermode.
70   * @prot: The effective protection on the mmap, resolved from reqprot and
71   *	  system configuration.
72   *
73   * This LSM hook is called when a mmap'd region of memory is changing
74   * its protections via mprotect.
75   *
76   * Return:
77   * * %0		- Success
78   * * %-EACCES	- Did not pass IPE policy
79   */
ipe_file_mprotect(struct vm_area_struct * vma,unsigned long reqprot __always_unused,unsigned long prot)80  int ipe_file_mprotect(struct vm_area_struct *vma,
81  		      unsigned long reqprot __always_unused,
82  		      unsigned long prot)
83  {
84  	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
85  
86  	/* Already Executable */
87  	if (vma->vm_flags & VM_EXEC)
88  		return 0;
89  
90  	if (prot & PROT_EXEC) {
91  		ipe_build_eval_ctx(&ctx, vma->vm_file, IPE_OP_EXEC, IPE_HOOK_MPROTECT);
92  		return ipe_evaluate_event(&ctx);
93  	}
94  
95  	return 0;
96  }
97  
98  /**
99   * ipe_kernel_read_file() - ipe security hook function for kernel read.
100   * @file: Supplies a pointer to the file structure being read in from disk.
101   * @id: Supplies the enumeration identifying the purpose of the read.
102   * @contents: Unused.
103   *
104   * This LSM hook is called when a file is read from disk in the kernel.
105   *
106   * Return:
107   * * %0		- Success
108   * * %-EACCES	- Did not pass IPE policy
109   */
ipe_kernel_read_file(struct file * file,enum kernel_read_file_id id,bool contents)110  int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
111  			 bool contents)
112  {
113  	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
114  	enum ipe_op_type op;
115  
116  	switch (id) {
117  	case READING_FIRMWARE:
118  		op = IPE_OP_FIRMWARE;
119  		break;
120  	case READING_MODULE:
121  		op = IPE_OP_KERNEL_MODULE;
122  		break;
123  	case READING_KEXEC_INITRAMFS:
124  		op = IPE_OP_KEXEC_INITRAMFS;
125  		break;
126  	case READING_KEXEC_IMAGE:
127  		op = IPE_OP_KEXEC_IMAGE;
128  		break;
129  	case READING_POLICY:
130  		op = IPE_OP_POLICY;
131  		break;
132  	case READING_X509_CERTIFICATE:
133  		op = IPE_OP_X509;
134  		break;
135  	default:
136  		op = IPE_OP_INVALID;
137  		WARN(1, "no rule setup for kernel_read_file enum %d", id);
138  	}
139  
140  	ipe_build_eval_ctx(&ctx, file, op, IPE_HOOK_KERNEL_READ);
141  	return ipe_evaluate_event(&ctx);
142  }
143  
144  /**
145   * ipe_kernel_load_data() - ipe security hook function for kernel load data.
146   * @id: Supplies the enumeration identifying the purpose of the load.
147   * @contents: Unused.
148   *
149   * This LSM hook is called when a data buffer provided by userspace is loading
150   * into the kernel.
151   *
152   * Return:
153   * * %0		- Success
154   * * %-EACCES	- Did not pass IPE policy
155   */
ipe_kernel_load_data(enum kernel_load_data_id id,bool contents)156  int ipe_kernel_load_data(enum kernel_load_data_id id, bool contents)
157  {
158  	struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
159  	enum ipe_op_type op;
160  
161  	switch (id) {
162  	case LOADING_FIRMWARE:
163  		op = IPE_OP_FIRMWARE;
164  		break;
165  	case LOADING_MODULE:
166  		op = IPE_OP_KERNEL_MODULE;
167  		break;
168  	case LOADING_KEXEC_INITRAMFS:
169  		op = IPE_OP_KEXEC_INITRAMFS;
170  		break;
171  	case LOADING_KEXEC_IMAGE:
172  		op = IPE_OP_KEXEC_IMAGE;
173  		break;
174  	case LOADING_POLICY:
175  		op = IPE_OP_POLICY;
176  		break;
177  	case LOADING_X509_CERTIFICATE:
178  		op = IPE_OP_X509;
179  		break;
180  	default:
181  		op = IPE_OP_INVALID;
182  		WARN(1, "no rule setup for kernel_load_data enum %d", id);
183  	}
184  
185  	ipe_build_eval_ctx(&ctx, NULL, op, IPE_HOOK_KERNEL_LOAD);
186  	return ipe_evaluate_event(&ctx);
187  }
188  
189  /**
190   * ipe_unpack_initramfs() - Mark the current rootfs as initramfs.
191   */
ipe_unpack_initramfs(void)192  void ipe_unpack_initramfs(void)
193  {
194  	ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true;
195  }
196  
197  #ifdef CONFIG_IPE_PROP_DM_VERITY
198  /**
199   * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices.
200   * @bdev: Supplies a pointer to a block_device that contains the structure
201   *	  to free.
202   */
ipe_bdev_free_security(struct block_device * bdev)203  void ipe_bdev_free_security(struct block_device *bdev)
204  {
205  	struct ipe_bdev *blob = ipe_bdev(bdev);
206  
207  	ipe_digest_free(blob->root_hash);
208  }
209  
210  #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
ipe_set_dmverity_signature(struct ipe_bdev * blob,const void * value,size_t size)211  static void ipe_set_dmverity_signature(struct ipe_bdev *blob,
212  				       const void *value,
213  				       size_t size)
214  {
215  	blob->dm_verity_signed = size > 0 && value;
216  }
217  #else
ipe_set_dmverity_signature(struct ipe_bdev * blob,const void * value,size_t size)218  static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob,
219  					      const void *value,
220  					      size_t size)
221  {
222  }
223  #endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
224  
225  /**
226   * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob.
227   * @bdev: Supplies a pointer to a block_device that contains the LSM blob.
228   * @type: Supplies the integrity type.
229   * @value: Supplies the value to store.
230   * @size: The size of @value.
231   *
232   * This hook is currently used to save dm-verity's root hash or the existence
233   * of a validated signed dm-verity root hash into LSM blob.
234   *
235   * Return: %0 on success. If an error occurs, the function will return the
236   * -errno.
237   */
ipe_bdev_setintegrity(struct block_device * bdev,enum lsm_integrity_type type,const void * value,size_t size)238  int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type,
239  			  const void *value, size_t size)
240  {
241  	const struct dm_verity_digest *digest = NULL;
242  	struct ipe_bdev *blob = ipe_bdev(bdev);
243  	struct digest_info *info = NULL;
244  
245  	if (type == LSM_INT_DMVERITY_SIG_VALID) {
246  		ipe_set_dmverity_signature(blob, value, size);
247  
248  		return 0;
249  	}
250  
251  	if (type != LSM_INT_DMVERITY_ROOTHASH)
252  		return -EINVAL;
253  
254  	if (!value) {
255  		ipe_digest_free(blob->root_hash);
256  		blob->root_hash = NULL;
257  
258  		return 0;
259  	}
260  	digest = value;
261  
262  	info = kzalloc(sizeof(*info), GFP_KERNEL);
263  	if (!info)
264  		return -ENOMEM;
265  
266  	info->digest = kmemdup(digest->digest, digest->digest_len, GFP_KERNEL);
267  	if (!info->digest)
268  		goto err;
269  
270  	info->alg = kstrdup(digest->alg, GFP_KERNEL);
271  	if (!info->alg)
272  		goto err;
273  
274  	info->digest_len = digest->digest_len;
275  
276  	ipe_digest_free(blob->root_hash);
277  	blob->root_hash = info;
278  
279  	return 0;
280  err:
281  	ipe_digest_free(info);
282  
283  	return -ENOMEM;
284  }
285  #endif /* CONFIG_IPE_PROP_DM_VERITY */
286  
287  #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
288  /**
289   * ipe_inode_setintegrity() - save integrity data from a inode to IPE's LSM blob.
290   * @inode: The inode to source the security blob from.
291   * @type: Supplies the integrity type.
292   * @value: The value to be stored.
293   * @size: The size of @value.
294   *
295   * This hook is currently used to save the existence of a validated fs-verity
296   * builtin signature into LSM blob.
297   *
298   * Return: %0 on success. If an error occurs, the function will return the
299   * -errno.
300   */
ipe_inode_setintegrity(const struct inode * inode,enum lsm_integrity_type type,const void * value,size_t size)301  int ipe_inode_setintegrity(const struct inode *inode,
302  			   enum lsm_integrity_type type,
303  			   const void *value, size_t size)
304  {
305  	struct ipe_inode *inode_sec = ipe_inode(inode);
306  
307  	if (type == LSM_INT_FSVERITY_BUILTINSIG_VALID) {
308  		inode_sec->fs_verity_signed = size > 0 && value;
309  		return 0;
310  	}
311  
312  	return -EINVAL;
313  }
314  #endif /* CONFIG_CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
315