• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2023 Huawei Device Co., Ltd.
4  */
5 
6 #include <linux/mman.h>
7 #include <linux/mm_types.h>
8 
9 #include "avc.h"
10 #include "objsec.h"
11 #include "exec_signature_info.h"
12 #include "fsverity_private.h"
13 #include "code_sign_ext.h"
14 #include "xpm_common.h"
15 #include "xpm_debugfs.h"
16 #include "xpm_log.h"
17 #include "xpm_report.h"
18 #include "xpm_security_hooks.h"
19 
20 enum ownerid_policy_type {
21 	DENY = 0,
22 	ALLOW,
23 	CHECK,
24 };
25 
26 static uint32_t ownerid_policy[PROCESS_OWNERID_MAX][FILE_OWNERID_MAX] __ro_after_init;
27 
init_ownerid_policy(void)28 static void init_ownerid_policy(void)
29 {
30 	ownerid_policy[PROCESS_OWNERID_SYSTEM][FILE_OWNERID_SYSTEM] = ALLOW;
31 
32 	ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_SYSTEM] = ALLOW;
33 	ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_SHARED] = ALLOW;
34 	ownerid_policy[PROCESS_OWNERID_APP][FILE_OWNERID_APP] = CHECK;
35 
36 	ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_SYSTEM] = ALLOW;
37 	ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_SHARED] = ALLOW;
38 	ownerid_policy[PROCESS_OWNERID_DEBUG][FILE_OWNERID_DEBUG] = ALLOW;
39 
40 	ownerid_policy[PROCESS_OWNERID_COMPAT][FILE_OWNERID_SYSTEM] = ALLOW;
41 	ownerid_policy[PROCESS_OWNERID_COMPAT][FILE_OWNERID_COMPAT] = ALLOW;
42 
43 	for (int i = 0; i < FILE_OWNERID_MAX; i++) {
44 		ownerid_policy[PROCESS_OWNERID_EXTEND][i] = ALLOW;
45 	}
46 }
47 
check_same_ownerid(struct cs_info * pcs_info,struct cs_info * fcs_info)48 static int check_same_ownerid(struct cs_info *pcs_info, struct cs_info *fcs_info)
49 {
50 	if ((pcs_info->id_type == fcs_info->id_type) &&
51 		(pcs_info->ownerid == fcs_info->ownerid)) {
52 		return 0;
53 	}
54 
55 	return -EPERM;
56 }
57 
xpm_check_ownerid_policy(struct cs_info * pcs_info,struct cs_info * fcs_info)58 int xpm_check_ownerid_policy(struct cs_info *pcs_info, struct cs_info *fcs_info)
59 {
60 	uint32_t type;
61 
62 	if (!pcs_info || !fcs_info) {
63 		xpm_log_error("input pcs_info or fcs_info is NULL");
64 		return -EINVAL;
65 	}
66 
67 	if ((pcs_info->id_type >= PROCESS_OWNERID_MAX) ||
68 		(fcs_info->id_type >= FILE_OWNERID_MAX)) {
69 		xpm_log_info("process or file ownerid exceed maximum value");
70 		return -EINVAL;
71 	}
72 
73 	type = ownerid_policy[pcs_info->id_type][fcs_info->id_type];
74 	switch (type) {
75 	case DENY:
76 		return -EPERM;
77 	case ALLOW:
78 		return 0;
79 	case CHECK:
80 		return check_same_ownerid(pcs_info, fcs_info);
81 	default:
82 		xpm_log_error("input ownerid type is invalid: %u", type);
83 		break;
84 	}
85 
86 	return -EINVAL;
87 }
88 
xpm_get_file_cs_info(struct cs_info * fcs_info,struct exec_file_signature_info * info)89 static int xpm_get_file_cs_info(struct cs_info *fcs_info,
90 	struct exec_file_signature_info *info)
91 {
92 	/* exec file is dm-verity */
93 	if (exec_file_signature_is_dm_verity(info)) {
94 		code_sign_set_ownerid(fcs_info, FILE_OWNERID_SYSTEM, NULL, 0);
95 		return 0;
96 	}
97 
98 	/* exec file is fs-verity */
99 	if (exec_file_signature_is_fs_verity(info)) {
100 		struct fsverity_info *vi = fsverity_get_info(info->inode);
101 		if (!vi) {
102 			xpm_log_error("get verity info failed in fs-verity");
103 			return -EINVAL;
104 		}
105 
106 		fcs_info->id_type = vi->fcs_info.id_type;
107 		fcs_info->ownerid = vi->fcs_info.ownerid;
108 		return 0;
109 	}
110 
111 	xpm_log_error("invalid code signature info type");
112 	return -EINVAL;
113 }
114 
xpm_get_process_cs_info(struct cs_info * pcs_info)115 int xpm_get_process_cs_info(struct cs_info *pcs_info)
116 {
117 	int ret;
118 	struct exec_file_signature_info *info = NULL;
119 	struct file *exe_file = NULL;
120 	struct cs_info fcs_info = {0};
121 	struct mm_struct *mm = current->mm;
122 
123 	if (!mm)
124 		return -EINVAL;
125 
126 	/* process cs_info has not been init, just init from exe file */
127 	if (mm->pcs_info.id_type == PROCESS_OWNERID_UNINIT) {
128 		exe_file = get_task_exe_file(current);
129 		if (!exe_file) {
130 			xpm_log_error("xpm get exe_file failed");
131 			return -ENOEXEC;
132 		}
133 
134 		ret = get_exec_file_signature_info(exe_file, true, &info);
135 		/* reduce exe_file reference count */
136 		fput(exe_file);
137 		if (ret || (info == NULL)) {
138 			xpm_log_error("xpm get exe_file signature info failed");
139 			return ret;
140 		}
141 
142 		ret = xpm_get_file_cs_info(&fcs_info, info);
143 		if (ret) {
144 			xpm_log_error("xpm get exe_file cs info failed");
145 			return ret;
146 		}
147 
148 		/* process's ownerid is correspond to file */
149 		mm->pcs_info.id_type = fcs_info.id_type;
150 		mm->pcs_info.ownerid = fcs_info.ownerid;
151 	}
152 	pcs_info->id_type = mm->pcs_info.id_type;
153 	pcs_info->ownerid = mm->pcs_info.ownerid;
154 
155 	return 0;
156 }
157 
xpm_check_ownerid(struct vm_area_struct * vma,struct exec_file_signature_info * info)158 static int xpm_check_ownerid(struct vm_area_struct *vma,
159 	struct exec_file_signature_info *info)
160 {
161 	int ret;
162 	struct cs_info pcs_info = {0};
163 	struct cs_info fcs_info = {0};
164 
165 	ret = xpm_get_process_cs_info(&pcs_info);
166 	if (ret) {
167 		xpm_log_error("xpm get process cs_info falied");
168 		return ret;
169 	}
170 
171 	ret = xpm_get_file_cs_info(&fcs_info, info);
172 	if (ret) {
173 		xpm_log_error("xpm get file cs_info falied");
174 		return ret;
175 	}
176 
177 	return xpm_check_ownerid_policy(&pcs_info, &fcs_info);
178 }
179 
xpm_avc_has_perm(u16 tclass,u32 requested)180 static int xpm_avc_has_perm(u16 tclass, u32 requested)
181 {
182 	struct av_decision avd;
183 	u32 sid = current_sid();
184 
185 	return avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
186 		AVC_STRICT, &avd);
187 }
188 
xpm_validate_signature(struct vm_area_struct * vma,struct exec_file_signature_info * info)189 static int xpm_validate_signature(struct vm_area_struct *vma,
190 	struct exec_file_signature_info *info)
191 {
192 	unsigned long verified_data_end, vm_addr_end;
193 	const struct inode *inode = (const struct inode *)info->inode;
194 
195 	if (IS_ERR_OR_NULL(info)) {
196 		xpm_log_error("signature info is NULL");
197 		return -EPERM;
198 	}
199 
200 	if(!exec_file_signature_is_fs_verity(info))
201 		return 0;
202 
203 	vm_addr_end = (vma->vm_pgoff << PAGE_SHIFT)
204 					+ (vma->vm_end - vma->vm_start);
205 	verified_data_end = PAGE_ALIGN(fsverity_get_verified_data_size(inode));
206 	if (verified_data_end < vm_addr_end) {
207 		xpm_log_error("data is out of verified data size");
208 		return -EPERM;
209 	}
210 
211 	return 0;
212 }
213 
xpm_check_code_segment(bool is_exec,struct vm_area_struct * vma,struct exec_file_signature_info * info)214 static int xpm_check_code_segment(bool is_exec, struct vm_area_struct *vma,
215 	struct exec_file_signature_info *info)
216 {
217 	int i;
218 	unsigned long vm_addr_start, vm_addr_end;
219 	unsigned long seg_addr_start, seg_addr_end;
220 	struct exec_segment_info *segments = info->code_segments;
221 
222 	if (!is_exec)
223 		return 0;
224 
225 	if (!segments) {
226 		xpm_log_error("code segments is NULL");
227 		return -EINVAL;
228 	}
229 
230 	vm_addr_start = vma->vm_pgoff << PAGE_SHIFT;
231 	vm_addr_end = vm_addr_start + (vma->vm_end - vma->vm_start);
232 
233 	for (i = 0; i < info->code_segment_count; i++) {
234 		seg_addr_start = ALIGN_DOWN(segments[i].file_offset, PAGE_SIZE);
235 		seg_addr_end = PAGE_ALIGN(segments[i].file_offset +
236 			segments[i].size);
237 		if ((vm_addr_start >= seg_addr_start) &&
238 			(vm_addr_end <= seg_addr_end))
239 			return 0;
240 	}
241 
242 	return -EPERM;
243 }
244 
xpm_check_signature(struct vm_area_struct * vma,unsigned long prot)245 static int xpm_check_signature(struct vm_area_struct *vma, unsigned long prot)
246 {
247 	int ret;
248 	bool is_exec;
249 	struct exec_file_signature_info *info = NULL;
250 
251 	/* vma is non-executable or mmap in xpm region just return */
252 	is_exec = !xpm_is_anonymous_vma(vma) && (prot & PROT_EXEC);
253 	if (!((vma->vm_flags & VM_XPM) || is_exec))
254 		return 0;
255 
256 	/* process has exec_no_sign permission just return */
257 	if (xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_NO_SIGN) == 0)
258 		return 0;
259 
260 	/* validate signature when vma is mmap in xpm region or executable */
261 	ret = get_exec_file_signature_info(vma->vm_file, is_exec, &info);
262 	if (ret) {
263 		report_mmap_event(GET_SIGN_FAIL, is_exec ? TYPE_ELF : TYPE_ABC,
264 			vma, prot);
265 		return ret;
266 	}
267 
268 	do {
269 		ret = xpm_validate_signature(vma, info);
270 		if (ret) {
271 			report_mmap_event(SIGN_INVALID,
272 				is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
273 			break;
274 		}
275 
276 		ret = xpm_check_code_segment(is_exec, vma, info);
277 		if (ret) {
278 			report_mmap_event(DATA_MMAP_CODE,
279 				is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
280 			break;
281 		}
282 
283 		ret = xpm_check_ownerid(vma, info);
284 		if (ret) {
285 			report_mmap_event(OWNERID_INCONSISTENT,
286 				is_exec ? TYPE_ELF : TYPE_ABC, vma, prot);
287 			break;
288 		}
289 	} while (0);
290 
291 	if (info)
292 		put_exec_file_signature_info(info);
293 
294 	return ret;
295 }
296 
xpm_check_prot(struct vm_area_struct * vma,unsigned long prot)297 static int xpm_check_prot(struct vm_area_struct *vma, unsigned long prot)
298 {
299 	int ret;
300 	bool is_anon;
301 
302 	is_anon = xpm_is_anonymous_vma(vma);
303 
304 	/* check for xpm region vma prot */
305 	if (vma->vm_flags & VM_XPM) {
306 		if (is_anon || (prot & PROT_EXEC)) {
307 			xpm_log_error("xpm region mmap not allow anonymous or exec permission");
308 			return -EPERM;
309 		}
310 
311 		return 0;
312 	}
313 
314 	/* check for anonymous vma prot, anonymous executable permission need
315 	 * controled by selinux
316 	 */
317 	if (is_anon && (prot & PROT_EXEC)) {
318 		ret = xpm_avc_has_perm(SECCLASS_XPM, XPM__EXEC_ANON_MEM);
319 		if (ret) {
320 			report_mmap_event(ANON_EXEC, TYPE_ANON, vma,  prot);
321 			return -EPERM;
322 		}
323 
324 		return 0;
325 	}
326 
327 	/* check for non-anonymous vma prot */
328 	if (!is_anon && (prot & PROT_WRITE) && (prot & PROT_EXEC)) {
329 		xpm_log_error("file mmap not allow write & exec permission");
330 		return -EPERM;
331 	}
332 
333 	return 0;
334 }
335 
xpm_common_check(struct vm_area_struct * vma,unsigned long prot)336 static int xpm_common_check(struct vm_area_struct *vma, unsigned long prot)
337 {
338 	int ret;
339 
340 	do {
341 		ret = xpm_check_prot(vma, prot);
342 		if (ret)
343 			break;
344 
345 		ret = xpm_check_signature(vma, prot);
346 	} while (0);
347 
348 	return xpm_ret(ret);
349 }
350 
xpm_mmap_check(struct vm_area_struct * vma)351 static int xpm_mmap_check(struct vm_area_struct *vma)
352 {
353 	return xpm_common_check(vma, vma->vm_flags);
354 }
355 
xpm_mprotect_check(struct vm_area_struct * vma,unsigned long reqprot,unsigned long prot)356 static int xpm_mprotect_check(struct vm_area_struct *vma,
357 	unsigned long reqprot, unsigned long prot)
358 {
359 	(void)reqprot;
360 
361 	return xpm_common_check(vma, prot);
362 }
363 
364 static struct security_hook_list xpm_hooks[] __lsm_ro_after_init = {
365 	LSM_HOOK_INIT(mmap_region, xpm_mmap_check),
366 	LSM_HOOK_INIT(file_mprotect, xpm_mprotect_check),
367 };
368 
xpm_register_security_hooks(void)369 void xpm_register_security_hooks(void)
370 {
371 	init_ownerid_policy();
372 	security_add_hooks(xpm_hooks, ARRAY_SIZE(xpm_hooks), "xpm");
373 }
374