1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * security/tomoyo/group.c |
4 | * |
5 | * Copyright (C) 2005-2011 NTT DATA CORPORATION |
6 | */ |
7 | |
8 | #include <linux/slab.h> |
9 | #include <linux/rculist.h> |
10 | |
11 | #include "common.h" |
12 | |
13 | /** |
14 | * tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_group" entry. |
15 | * |
16 | * @a: Pointer to "struct tomoyo_acl_head". |
17 | * @b: Pointer to "struct tomoyo_acl_head". |
18 | * |
19 | * Returns true if @a == @b, false otherwise. |
20 | */ |
21 | static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, |
22 | const struct tomoyo_acl_head *b) |
23 | { |
24 | return container_of(a, struct tomoyo_path_group, head)->member_name == |
25 | container_of(b, struct tomoyo_path_group, head)->member_name; |
26 | } |
27 | |
28 | /** |
29 | * tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_group" entry. |
30 | * |
31 | * @a: Pointer to "struct tomoyo_acl_head". |
32 | * @b: Pointer to "struct tomoyo_acl_head". |
33 | * |
34 | * Returns true if @a == @b, false otherwise. |
35 | */ |
36 | static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, |
37 | const struct tomoyo_acl_head *b) |
38 | { |
39 | return !memcmp(p: &container_of(a, struct tomoyo_number_group, head) |
40 | ->number, |
41 | q: &container_of(b, struct tomoyo_number_group, head) |
42 | ->number, |
43 | size: sizeof(container_of(a, struct tomoyo_number_group, head) |
44 | ->number)); |
45 | } |
46 | |
47 | /** |
48 | * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry. |
49 | * |
50 | * @a: Pointer to "struct tomoyo_acl_head". |
51 | * @b: Pointer to "struct tomoyo_acl_head". |
52 | * |
53 | * Returns true if @a == @b, false otherwise. |
54 | */ |
55 | static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a, |
56 | const struct tomoyo_acl_head *b) |
57 | { |
58 | const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1), |
59 | head); |
60 | const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2), |
61 | head); |
62 | |
63 | return tomoyo_same_ipaddr_union(a: &p1->address, b: &p2->address); |
64 | } |
65 | |
66 | /** |
67 | * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. |
68 | * |
69 | * @param: Pointer to "struct tomoyo_acl_param". |
70 | * @type: Type of this group. |
71 | * |
72 | * Returns 0 on success, negative value otherwise. |
73 | */ |
74 | int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type) |
75 | { |
76 | struct tomoyo_group *group = tomoyo_get_group(param, idx: type); |
77 | int error = -EINVAL; |
78 | |
79 | if (!group) |
80 | return -ENOMEM; |
81 | param->list = &group->member_list; |
82 | if (type == TOMOYO_PATH_GROUP) { |
83 | struct tomoyo_path_group e = { }; |
84 | |
85 | e.member_name = tomoyo_get_name(name: tomoyo_read_token(param)); |
86 | if (!e.member_name) { |
87 | error = -ENOMEM; |
88 | goto out; |
89 | } |
90 | error = tomoyo_update_policy(new_entry: &e.head, size: sizeof(e), param, |
91 | check_duplicate: tomoyo_same_path_group); |
92 | tomoyo_put_name(name: e.member_name); |
93 | } else if (type == TOMOYO_NUMBER_GROUP) { |
94 | struct tomoyo_number_group e = { }; |
95 | |
96 | if (param->data[0] == '@' || |
97 | !tomoyo_parse_number_union(param, ptr: &e.number)) |
98 | goto out; |
99 | error = tomoyo_update_policy(new_entry: &e.head, size: sizeof(e), param, |
100 | check_duplicate: tomoyo_same_number_group); |
101 | /* |
102 | * tomoyo_put_number_union() is not needed because |
103 | * param->data[0] != '@'. |
104 | */ |
105 | } else { |
106 | struct tomoyo_address_group e = { }; |
107 | |
108 | if (param->data[0] == '@' || |
109 | !tomoyo_parse_ipaddr_union(param, ptr: &e.address)) |
110 | goto out; |
111 | error = tomoyo_update_policy(new_entry: &e.head, size: sizeof(e), param, |
112 | check_duplicate: tomoyo_same_address_group); |
113 | } |
114 | out: |
115 | tomoyo_put_group(group); |
116 | return error; |
117 | } |
118 | |
119 | /** |
120 | * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. |
121 | * |
122 | * @pathname: The name of pathname. |
123 | * @group: Pointer to "struct tomoyo_path_group". |
124 | * |
125 | * Returns matched member's pathname if @pathname matches pathnames in @group, |
126 | * NULL otherwise. |
127 | * |
128 | * Caller holds tomoyo_read_lock(). |
129 | */ |
130 | const struct tomoyo_path_info * |
131 | tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, |
132 | const struct tomoyo_group *group) |
133 | { |
134 | struct tomoyo_path_group *member; |
135 | |
136 | list_for_each_entry_rcu(member, &group->member_list, head.list, |
137 | srcu_read_lock_held(&tomoyo_ss)) { |
138 | if (member->head.is_deleted) |
139 | continue; |
140 | if (!tomoyo_path_matches_pattern(filename: pathname, pattern: member->member_name)) |
141 | continue; |
142 | return member->member_name; |
143 | } |
144 | return NULL; |
145 | } |
146 | |
147 | /** |
148 | * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. |
149 | * |
150 | * @min: Min number. |
151 | * @max: Max number. |
152 | * @group: Pointer to "struct tomoyo_number_group". |
153 | * |
154 | * Returns true if @min and @max partially overlaps @group, false otherwise. |
155 | * |
156 | * Caller holds tomoyo_read_lock(). |
157 | */ |
158 | bool tomoyo_number_matches_group(const unsigned long min, |
159 | const unsigned long max, |
160 | const struct tomoyo_group *group) |
161 | { |
162 | struct tomoyo_number_group *member; |
163 | bool matched = false; |
164 | |
165 | list_for_each_entry_rcu(member, &group->member_list, head.list, |
166 | srcu_read_lock_held(&tomoyo_ss)) { |
167 | if (member->head.is_deleted) |
168 | continue; |
169 | if (min > member->number.values[1] || |
170 | max < member->number.values[0]) |
171 | continue; |
172 | matched = true; |
173 | break; |
174 | } |
175 | return matched; |
176 | } |
177 | |
178 | /** |
179 | * tomoyo_address_matches_group - Check whether the given address matches members of the given address group. |
180 | * |
181 | * @is_ipv6: True if @address is an IPv6 address. |
182 | * @address: An IPv4 or IPv6 address. |
183 | * @group: Pointer to "struct tomoyo_address_group". |
184 | * |
185 | * Returns true if @address matches addresses in @group group, false otherwise. |
186 | * |
187 | * Caller holds tomoyo_read_lock(). |
188 | */ |
189 | bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, |
190 | const struct tomoyo_group *group) |
191 | { |
192 | struct tomoyo_address_group *member; |
193 | bool matched = false; |
194 | const u8 size = is_ipv6 ? 16 : 4; |
195 | |
196 | list_for_each_entry_rcu(member, &group->member_list, head.list, |
197 | srcu_read_lock_held(&tomoyo_ss)) { |
198 | if (member->head.is_deleted) |
199 | continue; |
200 | if (member->address.is_ipv6 != is_ipv6) |
201 | continue; |
202 | if (memcmp(p: &member->address.ip[0], q: address, size) > 0 || |
203 | memcmp(p: address, q: &member->address.ip[1], size) > 0) |
204 | continue; |
205 | matched = true; |
206 | break; |
207 | } |
208 | return matched; |
209 | } |
210 | |