1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2021 Microsoft Corporation |
4 | * |
5 | * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com) |
6 | * |
7 | * Measure critical data structures maintained by SELinux |
8 | * using IMA subsystem. |
9 | */ |
10 | #include <linux/vmalloc.h> |
11 | #include <linux/ima.h> |
12 | #include "security.h" |
13 | #include "ima.h" |
14 | |
15 | /* |
16 | * selinux_ima_collect_state - Read selinux configuration settings |
17 | * |
18 | * On success returns the configuration settings string. |
19 | * On error, returns NULL. |
20 | */ |
21 | static char *selinux_ima_collect_state(void) |
22 | { |
23 | const char *on = "=1;" , *off = "=0;" ; |
24 | char *buf; |
25 | int buf_len, len, i, rc; |
26 | |
27 | buf_len = strlen("initialized=0;enforcing=0;checkreqprot=0;" ) + 1; |
28 | |
29 | len = strlen(on); |
30 | for (i = 0; i < __POLICYDB_CAP_MAX; i++) |
31 | buf_len += strlen(selinux_policycap_names[i]) + len; |
32 | |
33 | buf = kzalloc(size: buf_len, GFP_KERNEL); |
34 | if (!buf) |
35 | return NULL; |
36 | |
37 | rc = strscpy(buf, "initialized" , buf_len); |
38 | WARN_ON(rc < 0); |
39 | |
40 | rc = strlcat(p: buf, q: selinux_initialized() ? on : off, avail: buf_len); |
41 | WARN_ON(rc >= buf_len); |
42 | |
43 | rc = strlcat(p: buf, q: "enforcing" , avail: buf_len); |
44 | WARN_ON(rc >= buf_len); |
45 | |
46 | rc = strlcat(p: buf, q: enforcing_enabled() ? on : off, avail: buf_len); |
47 | WARN_ON(rc >= buf_len); |
48 | |
49 | rc = strlcat(p: buf, q: "checkreqprot" , avail: buf_len); |
50 | WARN_ON(rc >= buf_len); |
51 | |
52 | rc = strlcat(p: buf, q: checkreqprot_get() ? on : off, avail: buf_len); |
53 | WARN_ON(rc >= buf_len); |
54 | |
55 | for (i = 0; i < __POLICYDB_CAP_MAX; i++) { |
56 | rc = strlcat(buf, selinux_policycap_names[i], buf_len); |
57 | WARN_ON(rc >= buf_len); |
58 | |
59 | rc = strlcat(buf, selinux_state.policycap[i] ? on : off, |
60 | buf_len); |
61 | WARN_ON(rc >= buf_len); |
62 | } |
63 | |
64 | return buf; |
65 | } |
66 | |
67 | /* |
68 | * selinux_ima_measure_state_locked - Measure SELinux state and hash of policy |
69 | */ |
70 | void selinux_ima_measure_state_locked(void) |
71 | { |
72 | char *state_str = NULL; |
73 | void *policy = NULL; |
74 | size_t policy_len; |
75 | int rc = 0; |
76 | |
77 | lockdep_assert_held(&selinux_state.policy_mutex); |
78 | |
79 | state_str = selinux_ima_collect_state(); |
80 | if (!state_str) { |
81 | pr_err("SELinux: %s: failed to read state.\n" , __func__); |
82 | return; |
83 | } |
84 | |
85 | ima_measure_critical_data(event_label: "selinux" , event_name: "selinux-state" , |
86 | buf: state_str, strlen(state_str), hash: false, |
87 | NULL, digest_len: 0); |
88 | |
89 | kfree(objp: state_str); |
90 | |
91 | /* |
92 | * Measure SELinux policy only after initialization is completed. |
93 | */ |
94 | if (!selinux_initialized()) |
95 | return; |
96 | |
97 | rc = security_read_state_kernel(&policy, &policy_len); |
98 | if (rc) { |
99 | pr_err("SELinux: %s: failed to read policy %d.\n" , __func__, rc); |
100 | return; |
101 | } |
102 | |
103 | ima_measure_critical_data(event_label: "selinux" , event_name: "selinux-policy-hash" , |
104 | buf: policy, buf_len: policy_len, hash: true, |
105 | NULL, digest_len: 0); |
106 | |
107 | vfree(addr: policy); |
108 | } |
109 | |
110 | /* |
111 | * selinux_ima_measure_state - Measure SELinux state and hash of policy |
112 | */ |
113 | void selinux_ima_measure_state(void) |
114 | { |
115 | lockdep_assert_not_held(&selinux_state.policy_mutex); |
116 | |
117 | mutex_lock(&selinux_state.policy_mutex); |
118 | selinux_ima_measure_state_locked(); |
119 | mutex_unlock(lock: &selinux_state.policy_mutex); |
120 | } |
121 | |