Commit | Line | Data |
---|---|---|
cb72d382 HD |
1 | /* |
2 | * CALIPSO - Common Architecture Label IPv6 Security Option | |
3 | * | |
4 | * This is an implementation of the CALIPSO protocol as specified in | |
5 | * RFC 5570. | |
6 | * | |
7 | * Authors: Paul Moore <paul.moore@hp.com> | |
8 | * Huw Davies <huw@codeweavers.com> | |
9 | * | |
10 | */ | |
11 | ||
12 | /* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 | |
13 | * (c) Copyright Huw Davies <huw@codeweavers.com>, 2015 | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
23 | * the GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
27 | * | |
28 | */ | |
29 | ||
30 | #include <linux/init.h> | |
31 | #include <linux/types.h> | |
32 | #include <linux/rcupdate.h> | |
33 | #include <linux/list.h> | |
34 | #include <linux/spinlock.h> | |
35 | #include <linux/string.h> | |
36 | #include <linux/jhash.h> | |
37 | #include <linux/audit.h> | |
38 | #include <linux/slab.h> | |
39 | #include <net/ip.h> | |
40 | #include <net/icmp.h> | |
41 | #include <net/tcp.h> | |
42 | #include <net/netlabel.h> | |
43 | #include <net/calipso.h> | |
44 | #include <linux/atomic.h> | |
45 | #include <linux/bug.h> | |
46 | #include <asm/unaligned.h> | |
47 | ||
48 | /* List of available DOI definitions */ | |
49 | static DEFINE_SPINLOCK(calipso_doi_list_lock); | |
50 | static LIST_HEAD(calipso_doi_list); | |
51 | ||
52 | /* DOI List Functions | |
53 | */ | |
54 | ||
55 | /** | |
56 | * calipso_doi_search - Searches for a DOI definition | |
57 | * @doi: the DOI to search for | |
58 | * | |
59 | * Description: | |
60 | * Search the DOI definition list for a DOI definition with a DOI value that | |
61 | * matches @doi. The caller is responsible for calling rcu_read_[un]lock(). | |
62 | * Returns a pointer to the DOI definition on success and NULL on failure. | |
63 | */ | |
64 | static struct calipso_doi *calipso_doi_search(u32 doi) | |
65 | { | |
66 | struct calipso_doi *iter; | |
67 | ||
68 | list_for_each_entry_rcu(iter, &calipso_doi_list, list) | |
69 | if (iter->doi == doi && atomic_read(&iter->refcount)) | |
70 | return iter; | |
71 | return NULL; | |
72 | } | |
73 | ||
74 | /** | |
75 | * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine | |
76 | * @doi_def: the DOI structure | |
77 | * @audit_info: NetLabel audit information | |
78 | * | |
79 | * Description: | |
80 | * The caller defines a new DOI for use by the CALIPSO engine and calls this | |
81 | * function to add it to the list of acceptable domains. The caller must | |
82 | * ensure that the mapping table specified in @doi_def->map meets all of the | |
83 | * requirements of the mapping type (see calipso.h for details). Returns | |
84 | * zero on success and non-zero on failure. | |
85 | * | |
86 | */ | |
87 | static int calipso_doi_add(struct calipso_doi *doi_def, | |
88 | struct netlbl_audit *audit_info) | |
89 | { | |
90 | int ret_val = -EINVAL; | |
91 | u32 doi; | |
92 | u32 doi_type; | |
93 | struct audit_buffer *audit_buf; | |
94 | ||
95 | doi = doi_def->doi; | |
96 | doi_type = doi_def->type; | |
97 | ||
98 | if (doi_def->doi == CALIPSO_DOI_UNKNOWN) | |
99 | goto doi_add_return; | |
100 | ||
101 | atomic_set(&doi_def->refcount, 1); | |
102 | ||
103 | spin_lock(&calipso_doi_list_lock); | |
104 | if (calipso_doi_search(doi_def->doi)) { | |
105 | spin_unlock(&calipso_doi_list_lock); | |
106 | ret_val = -EEXIST; | |
107 | goto doi_add_return; | |
108 | } | |
109 | list_add_tail_rcu(&doi_def->list, &calipso_doi_list); | |
110 | spin_unlock(&calipso_doi_list_lock); | |
111 | ret_val = 0; | |
112 | ||
113 | doi_add_return: | |
114 | audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info); | |
115 | if (audit_buf) { | |
116 | const char *type_str; | |
117 | ||
118 | switch (doi_type) { | |
119 | case CALIPSO_MAP_PASS: | |
120 | type_str = "pass"; | |
121 | break; | |
122 | default: | |
123 | type_str = "(unknown)"; | |
124 | } | |
125 | audit_log_format(audit_buf, | |
126 | " calipso_doi=%u calipso_type=%s res=%u", | |
127 | doi, type_str, ret_val == 0 ? 1 : 0); | |
128 | audit_log_end(audit_buf); | |
129 | } | |
130 | ||
131 | return ret_val; | |
132 | } | |
133 | ||
134 | /** | |
135 | * calipso_doi_free - Frees a DOI definition | |
136 | * @doi_def: the DOI definition | |
137 | * | |
138 | * Description: | |
139 | * This function frees all of the memory associated with a DOI definition. | |
140 | * | |
141 | */ | |
142 | static void calipso_doi_free(struct calipso_doi *doi_def) | |
143 | { | |
144 | kfree(doi_def); | |
145 | } | |
146 | ||
a5e34490 HD |
147 | /** |
148 | * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer | |
149 | * @entry: the entry's RCU field | |
150 | * | |
151 | * Description: | |
152 | * This function is designed to be used as a callback to the call_rcu() | |
153 | * function so that the memory allocated to the DOI definition can be released | |
154 | * safely. | |
155 | * | |
156 | */ | |
157 | static void calipso_doi_free_rcu(struct rcu_head *entry) | |
158 | { | |
159 | struct calipso_doi *doi_def; | |
160 | ||
161 | doi_def = container_of(entry, struct calipso_doi, rcu); | |
162 | calipso_doi_free(doi_def); | |
163 | } | |
164 | ||
d7cce015 HD |
165 | /** |
166 | * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine | |
167 | * @doi: the DOI value | |
168 | * @audit_secid: the LSM secid to use in the audit message | |
169 | * | |
170 | * Description: | |
171 | * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will | |
172 | * be called to release their own LSM domain mappings as well as our own | |
173 | * domain list. Returns zero on success and negative values on failure. | |
174 | * | |
175 | */ | |
176 | static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) | |
177 | { | |
178 | int ret_val; | |
179 | struct calipso_doi *doi_def; | |
180 | struct audit_buffer *audit_buf; | |
181 | ||
182 | spin_lock(&calipso_doi_list_lock); | |
183 | doi_def = calipso_doi_search(doi); | |
184 | if (!doi_def) { | |
185 | spin_unlock(&calipso_doi_list_lock); | |
186 | ret_val = -ENOENT; | |
187 | goto doi_remove_return; | |
188 | } | |
189 | if (!atomic_dec_and_test(&doi_def->refcount)) { | |
190 | spin_unlock(&calipso_doi_list_lock); | |
191 | ret_val = -EBUSY; | |
192 | goto doi_remove_return; | |
193 | } | |
194 | list_del_rcu(&doi_def->list); | |
195 | spin_unlock(&calipso_doi_list_lock); | |
196 | ||
197 | call_rcu(&doi_def->rcu, calipso_doi_free_rcu); | |
198 | ret_val = 0; | |
199 | ||
200 | doi_remove_return: | |
201 | audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info); | |
202 | if (audit_buf) { | |
203 | audit_log_format(audit_buf, | |
204 | " calipso_doi=%u res=%u", | |
205 | doi, ret_val == 0 ? 1 : 0); | |
206 | audit_log_end(audit_buf); | |
207 | } | |
208 | ||
209 | return ret_val; | |
210 | } | |
211 | ||
a5e34490 HD |
212 | /** |
213 | * calipso_doi_getdef - Returns a reference to a valid DOI definition | |
214 | * @doi: the DOI value | |
215 | * | |
216 | * Description: | |
217 | * Searches for a valid DOI definition and if one is found it is returned to | |
218 | * the caller. Otherwise NULL is returned. The caller must ensure that | |
219 | * calipso_doi_putdef() is called when the caller is done. | |
220 | * | |
221 | */ | |
222 | static struct calipso_doi *calipso_doi_getdef(u32 doi) | |
223 | { | |
224 | struct calipso_doi *doi_def; | |
225 | ||
226 | rcu_read_lock(); | |
227 | doi_def = calipso_doi_search(doi); | |
228 | if (!doi_def) | |
229 | goto doi_getdef_return; | |
230 | if (!atomic_inc_not_zero(&doi_def->refcount)) | |
231 | doi_def = NULL; | |
232 | ||
233 | doi_getdef_return: | |
234 | rcu_read_unlock(); | |
235 | return doi_def; | |
236 | } | |
237 | ||
238 | /** | |
239 | * calipso_doi_putdef - Releases a reference for the given DOI definition | |
240 | * @doi_def: the DOI definition | |
241 | * | |
242 | * Description: | |
243 | * Releases a DOI definition reference obtained from calipso_doi_getdef(). | |
244 | * | |
245 | */ | |
246 | static void calipso_doi_putdef(struct calipso_doi *doi_def) | |
247 | { | |
248 | if (!doi_def) | |
249 | return; | |
250 | ||
251 | if (!atomic_dec_and_test(&doi_def->refcount)) | |
252 | return; | |
253 | spin_lock(&calipso_doi_list_lock); | |
254 | list_del_rcu(&doi_def->list); | |
255 | spin_unlock(&calipso_doi_list_lock); | |
256 | ||
257 | call_rcu(&doi_def->rcu, calipso_doi_free_rcu); | |
258 | } | |
259 | ||
e1ce69df HD |
260 | /** |
261 | * calipso_doi_walk - Iterate through the DOI definitions | |
262 | * @skip_cnt: skip past this number of DOI definitions, updated | |
263 | * @callback: callback for each DOI definition | |
264 | * @cb_arg: argument for the callback function | |
265 | * | |
266 | * Description: | |
267 | * Iterate over the DOI definition list, skipping the first @skip_cnt entries. | |
268 | * For each entry call @callback, if @callback returns a negative value stop | |
269 | * 'walking' through the list and return. Updates the value in @skip_cnt upon | |
270 | * return. Returns zero on success, negative values on failure. | |
271 | * | |
272 | */ | |
273 | static int calipso_doi_walk(u32 *skip_cnt, | |
274 | int (*callback)(struct calipso_doi *doi_def, | |
275 | void *arg), | |
276 | void *cb_arg) | |
277 | { | |
278 | int ret_val = -ENOENT; | |
279 | u32 doi_cnt = 0; | |
280 | struct calipso_doi *iter_doi; | |
281 | ||
282 | rcu_read_lock(); | |
283 | list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list) | |
284 | if (atomic_read(&iter_doi->refcount) > 0) { | |
285 | if (doi_cnt++ < *skip_cnt) | |
286 | continue; | |
287 | ret_val = callback(iter_doi, cb_arg); | |
288 | if (ret_val < 0) { | |
289 | doi_cnt--; | |
290 | goto doi_walk_return; | |
291 | } | |
292 | } | |
293 | ||
294 | doi_walk_return: | |
295 | rcu_read_unlock(); | |
296 | *skip_cnt = doi_cnt; | |
297 | return ret_val; | |
298 | } | |
299 | ||
cb72d382 HD |
300 | static const struct netlbl_calipso_ops ops = { |
301 | .doi_add = calipso_doi_add, | |
302 | .doi_free = calipso_doi_free, | |
d7cce015 | 303 | .doi_remove = calipso_doi_remove, |
a5e34490 HD |
304 | .doi_getdef = calipso_doi_getdef, |
305 | .doi_putdef = calipso_doi_putdef, | |
e1ce69df | 306 | .doi_walk = calipso_doi_walk, |
cb72d382 HD |
307 | }; |
308 | ||
309 | /** | |
310 | * calipso_init - Initialize the CALIPSO module | |
311 | * | |
312 | * Description: | |
313 | * Initialize the CALIPSO module and prepare it for use. Returns zero on | |
314 | * success and negative values on failure. | |
315 | * | |
316 | */ | |
317 | int __init calipso_init(void) | |
318 | { | |
319 | netlbl_calipso_ops_register(&ops); | |
320 | return 0; | |
321 | } | |
322 | ||
323 | void calipso_exit(void) | |
324 | { | |
325 | netlbl_calipso_ops_register(NULL); | |
326 | } |