1 |
/* |
2 |
* security/ccsecurity/group.c |
3 |
* |
4 |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.8.0-pre 2010/09/01 |
7 |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
10 |
* |
11 |
*/ |
12 |
|
13 |
#include "internal.h" |
14 |
|
15 |
static bool ccs_same_path_group(const struct ccs_acl_head *a, |
16 |
const struct ccs_acl_head *b) |
17 |
{ |
18 |
return container_of(a, struct ccs_path_group, head)->member_name == |
19 |
container_of(b, struct ccs_path_group, head)->member_name; |
20 |
} |
21 |
|
22 |
static bool ccs_same_number_group(const struct ccs_acl_head *a, |
23 |
const struct ccs_acl_head *b) |
24 |
{ |
25 |
return !memcmp(&container_of(a, struct ccs_number_group, head)->number, |
26 |
&container_of(b, struct ccs_number_group, head)->number, |
27 |
sizeof(container_of(a, struct ccs_number_group, head) |
28 |
->number)); |
29 |
} |
30 |
|
31 |
static bool ccs_same_address_group(const struct ccs_acl_head *a, |
32 |
const struct ccs_acl_head *b) |
33 |
{ |
34 |
const struct ccs_address_group *p1 = container_of(a, typeof(*p1), |
35 |
head); |
36 |
const struct ccs_address_group *p2 = container_of(b, typeof(*p2), |
37 |
head); |
38 |
return p1->is_ipv6 == p2->is_ipv6 && |
39 |
p1->min.ipv4 == p2->min.ipv4 && p1->min.ipv6 == p2->min.ipv6 && |
40 |
p1->max.ipv4 == p2->max.ipv4 && p1->max.ipv6 == p2->max.ipv6; |
41 |
} |
42 |
|
43 |
/** |
44 |
* ccs_write_group - Write "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. |
45 |
* |
46 |
* @data: String to parse. |
47 |
* @is_delete: True if it is a delete request. |
48 |
* @type: Type of this group. |
49 |
* |
50 |
* Returns 0 on success, negative value otherwise. |
51 |
*/ |
52 |
int ccs_write_group(char *data, const bool is_delete, const u8 type) |
53 |
{ |
54 |
struct ccs_group *group; |
55 |
struct list_head *member; |
56 |
char *w[2]; |
57 |
int error = -EINVAL; |
58 |
if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0]) |
59 |
return -EINVAL; |
60 |
group = ccs_get_group(w[0], type); |
61 |
if (!group) |
62 |
return -ENOMEM; |
63 |
member = &group->member_list; |
64 |
if (type == CCS_PATH_GROUP) { |
65 |
struct ccs_path_group e = { }; |
66 |
e.member_name = ccs_get_name(w[1]); |
67 |
if (!e.member_name) { |
68 |
error = -ENOMEM; |
69 |
goto out; |
70 |
} |
71 |
error = ccs_update_policy(&e.head, sizeof(e), is_delete, |
72 |
member, ccs_same_path_group); |
73 |
ccs_put_name(e.member_name); |
74 |
} else if (type == CCS_NUMBER_GROUP) { |
75 |
struct ccs_number_group e = { }; |
76 |
if (w[1][0] == '@' || !ccs_parse_number_union(w[1], &e.number) |
77 |
|| e.number.values[0] > e.number.values[1]) |
78 |
goto out; |
79 |
error = ccs_update_policy(&e.head, sizeof(e), is_delete, |
80 |
member, ccs_same_number_group); |
81 |
/* |
82 |
* ccs_put_number_union() is not needed because w[1][0] != '@'. |
83 |
*/ |
84 |
} else { |
85 |
struct ccs_address_group e = { }; |
86 |
u16 min_address[8]; |
87 |
u16 max_address[8]; |
88 |
switch (ccs_parse_ip_address(w[1], min_address, max_address)) { |
89 |
case CCS_IP_ADDRESS_TYPE_IPv6: |
90 |
e.is_ipv6 = true; |
91 |
e.min.ipv6 = ccs_get_ipv6_address((struct in6_addr *) |
92 |
min_address); |
93 |
e.max.ipv6 = ccs_get_ipv6_address((struct in6_addr *) |
94 |
max_address); |
95 |
if (!e.min.ipv6 || !e.max.ipv6) |
96 |
goto out_address; |
97 |
break; |
98 |
case CCS_IP_ADDRESS_TYPE_IPv4: |
99 |
e.min.ipv4 = ntohl(*(u32 *) min_address); |
100 |
e.max.ipv4 = ntohl(*(u32 *) max_address); |
101 |
break; |
102 |
default: |
103 |
goto out_address; |
104 |
} |
105 |
error = ccs_update_policy(&e.head, sizeof(e), is_delete, |
106 |
member, ccs_same_address_group); |
107 |
out_address: |
108 |
if (e.is_ipv6) { |
109 |
ccs_put_ipv6_address(e.min.ipv6); |
110 |
ccs_put_ipv6_address(e.max.ipv6); |
111 |
} |
112 |
} |
113 |
out: |
114 |
ccs_put_group(group); |
115 |
return error; |
116 |
} |
117 |
|
118 |
/** |
119 |
* ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. |
120 |
* |
121 |
* @pathname: The name of pathname. |
122 |
* @group: Pointer to "struct ccs_path_group". |
123 |
* |
124 |
* Returns matched member's pathname if @pathname matches pathnames in @group, |
125 |
* NULL otherwise. |
126 |
* |
127 |
* Caller holds ccs_read_lock(). |
128 |
*/ |
129 |
const struct ccs_path_info * |
130 |
ccs_path_matches_group(const struct ccs_path_info *pathname, |
131 |
const struct ccs_group *group) |
132 |
{ |
133 |
struct ccs_path_group *member; |
134 |
list_for_each_entry_rcu(member, &group->member_list, head.list) { |
135 |
if (member->head.is_deleted) |
136 |
continue; |
137 |
if (!ccs_path_matches_pattern(pathname, member->member_name)) |
138 |
continue; |
139 |
return member->member_name; |
140 |
} |
141 |
return NULL; |
142 |
} |
143 |
|
144 |
/** |
145 |
* ccs_number_matches_group - Check whether the given number matches members of the given number group. |
146 |
* |
147 |
* @min: Min number. |
148 |
* @max: Max number. |
149 |
* @group: Pointer to "struct ccs_number_group". |
150 |
* |
151 |
* Returns true if @min and @max partially overlaps @group, false otherwise. |
152 |
* |
153 |
* Caller holds ccs_read_lock(). |
154 |
*/ |
155 |
bool ccs_number_matches_group(const unsigned long min, const unsigned long max, |
156 |
const struct ccs_group *group) |
157 |
{ |
158 |
struct ccs_number_group *member; |
159 |
bool matched = false; |
160 |
list_for_each_entry_rcu(member, &group->member_list, head.list) { |
161 |
if (member->head.is_deleted) |
162 |
continue; |
163 |
if (min > member->number.values[1] || |
164 |
max < member->number.values[0]) |
165 |
continue; |
166 |
matched = true; |
167 |
break; |
168 |
} |
169 |
return matched; |
170 |
} |
171 |
|
172 |
/** |
173 |
* ccs_address_matches_group - Check whether the given address matches members of the given address group. |
174 |
* |
175 |
* @is_ipv6: True if @address is an IPv6 address. |
176 |
* @address: An IPv4 or IPv6 address. |
177 |
* @group: Pointer to "struct ccs_address_group". |
178 |
* |
179 |
* Returns true if @address matches addresses in @group group, false otherwise. |
180 |
* |
181 |
* Caller holds ccs_read_lock(). |
182 |
*/ |
183 |
bool ccs_address_matches_group(const bool is_ipv6, const u32 *address, |
184 |
const struct ccs_group *group) |
185 |
{ |
186 |
struct ccs_address_group *member; |
187 |
const u32 ip = ntohl(*address); |
188 |
bool matched = false; |
189 |
list_for_each_entry_rcu(member, &group->member_list, head.list) { |
190 |
if (member->head.is_deleted) |
191 |
continue; |
192 |
if (member->is_ipv6) { |
193 |
if (is_ipv6 && |
194 |
memcmp(member->min.ipv6, address, 16) <= 0 && |
195 |
memcmp(address, member->max.ipv6, 16) <= 0) { |
196 |
matched = true; |
197 |
break; |
198 |
} |
199 |
} else { |
200 |
if (!is_ipv6 && |
201 |
member->min.ipv4 <= ip && ip <= member->max.ipv4) { |
202 |
matched = true; |
203 |
break; |
204 |
} |
205 |
} |
206 |
} |
207 |
return matched; |
208 |
} |