Commit | Line | Data |
---|---|---|
a10e763b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * Implementation of the security services. | |
4 | * | |
0fe53224 | 5 | * Authors : Stephen Smalley, <stephen.smalley.work@gmail.com> |
5d55a345 | 6 | * James Morris <jmorris@redhat.com> |
1da177e4 LT |
7 | * |
8 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | |
9 | * | |
10 | * Support for enhanced MLS infrastructure. | |
376bd9cb | 11 | * Support for context based audit filters. |
1da177e4 LT |
12 | * |
13 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | |
14 | * | |
5d55a345 | 15 | * Added conditional policy language extensions |
1da177e4 | 16 | * |
82c21bfa | 17 | * Updated: Hewlett-Packard <paul@paul-moore.com> |
7420ed23 VY |
18 | * |
19 | * Added support for NetLabel | |
3bb56b25 | 20 | * Added support for the policy capability bitmap |
7420ed23 | 21 | * |
b94c7e67 CS |
22 | * Updated: Chad Sellers <csellers@tresys.com> |
23 | * | |
24 | * Added validation of kernel classes and permissions | |
25 | * | |
44c2d9bd KK |
26 | * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com> |
27 | * | |
28 | * Added support for bounds domain and audit messaged on masked permissions | |
29 | * | |
0719aaf5 GT |
30 | * Updated: Guido Trentalancia <guido@trentalancia.com> |
31 | * | |
32 | * Added support for runtime switching of the policy type | |
33 | * | |
44c2d9bd | 34 | * Copyright (C) 2008, 2009 NEC Corporation |
3bb56b25 | 35 | * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P. |
376bd9cb | 36 | * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. |
b94c7e67 | 37 | * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC |
1da177e4 | 38 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> |
1da177e4 LT |
39 | */ |
40 | #include <linux/kernel.h> | |
41 | #include <linux/slab.h> | |
42 | #include <linux/string.h> | |
43 | #include <linux/spinlock.h> | |
9f2ad665 | 44 | #include <linux/rcupdate.h> |
1da177e4 LT |
45 | #include <linux/errno.h> |
46 | #include <linux/in.h> | |
47 | #include <linux/sched.h> | |
48 | #include <linux/audit.h> | |
f0d3d989 | 49 | #include <linux/vmalloc.h> |
1aea7808 | 50 | #include <linux/lsm_hooks.h> |
7420ed23 | 51 | #include <net/netlabel.h> |
bb003079 | 52 | |
1da177e4 LT |
53 | #include "flask.h" |
54 | #include "avc.h" | |
55 | #include "avc_ss.h" | |
56 | #include "security.h" | |
57 | #include "context.h" | |
58 | #include "policydb.h" | |
59 | #include "sidtab.h" | |
60 | #include "services.h" | |
61 | #include "conditional.h" | |
62 | #include "mls.h" | |
7420ed23 | 63 | #include "objsec.h" |
c60475bf | 64 | #include "netlabel.h" |
3de4bab5 | 65 | #include "xfrm.h" |
02752760 | 66 | #include "ebitmap.h" |
9d57a7f9 | 67 | #include "audit.h" |
339949be | 68 | #include "policycap_names.h" |
fdd1ffe8 | 69 | #include "ima.h" |
4dc2fce3 | 70 | |
6406887a OM |
71 | struct selinux_policy_convert_data { |
72 | struct convert_context_args args; | |
73 | struct sidtab_convert_params sidtab_params; | |
74 | }; | |
75 | ||
1da177e4 | 76 | /* Forward declaration. */ |
aa8e712c SS |
77 | static int context_struct_to_string(struct policydb *policydb, |
78 | struct context *context, | |
79 | char **scontext, | |
1da177e4 LT |
80 | u32 *scontext_len); |
81 | ||
d97bd23c OM |
82 | static int sidtab_entry_to_string(struct policydb *policydb, |
83 | struct sidtab *sidtab, | |
84 | struct sidtab_entry *entry, | |
85 | char **scontext, | |
86 | u32 *scontext_len); | |
87 | ||
aa8e712c SS |
88 | static void context_struct_compute_av(struct policydb *policydb, |
89 | struct context *scontext, | |
90 | struct context *tcontext, | |
91 | u16 tclass, | |
92 | struct av_decision *avd, | |
93 | struct extended_perms *xperms); | |
c6d3aaa4 SS |
94 | |
95 | static int selinux_set_mapping(struct policydb *pol, | |
ded34574 | 96 | const struct security_class_mapping *map, |
aa8e712c | 97 | struct selinux_map *out_map) |
c6d3aaa4 | 98 | { |
c6d3aaa4 | 99 | u16 i, j; |
c6d3aaa4 SS |
100 | bool print_unknown_handle = false; |
101 | ||
102 | /* Find number of classes in the input mapping */ | |
103 | if (!map) | |
104 | return -EINVAL; | |
105 | i = 0; | |
106 | while (map[i].name) | |
107 | i++; | |
108 | ||
109 | /* Allocate space for the class records, plus one for class zero */ | |
aa8e712c SS |
110 | out_map->mapping = kcalloc(++i, sizeof(*out_map->mapping), GFP_ATOMIC); |
111 | if (!out_map->mapping) | |
c6d3aaa4 SS |
112 | return -ENOMEM; |
113 | ||
114 | /* Store the raw class and permission values */ | |
115 | j = 0; | |
116 | while (map[j].name) { | |
ded34574 | 117 | const struct security_class_mapping *p_in = map + (j++); |
aa8e712c | 118 | struct selinux_mapping *p_out = out_map->mapping + j; |
002903e1 | 119 | u16 k; |
c6d3aaa4 SS |
120 | |
121 | /* An empty class string skips ahead */ | |
122 | if (!strcmp(p_in->name, "")) { | |
123 | p_out->num_perms = 0; | |
124 | continue; | |
125 | } | |
126 | ||
127 | p_out->value = string_to_security_class(pol, p_in->name); | |
128 | if (!p_out->value) { | |
b54c85c1 | 129 | pr_info("SELinux: Class %s not defined in policy.\n", |
c6d3aaa4 SS |
130 | p_in->name); |
131 | if (pol->reject_unknown) | |
132 | goto err; | |
133 | p_out->num_perms = 0; | |
134 | print_unknown_handle = true; | |
135 | continue; | |
136 | } | |
137 | ||
138 | k = 0; | |
342e9157 | 139 | while (p_in->perms[k]) { |
c6d3aaa4 SS |
140 | /* An empty permission string skips ahead */ |
141 | if (!*p_in->perms[k]) { | |
142 | k++; | |
143 | continue; | |
144 | } | |
145 | p_out->perms[k] = string_to_av_perm(pol, p_out->value, | |
146 | p_in->perms[k]); | |
147 | if (!p_out->perms[k]) { | |
b54c85c1 | 148 | pr_info("SELinux: Permission %s in class %s not defined in policy.\n", |
c6d3aaa4 SS |
149 | p_in->perms[k], p_in->name); |
150 | if (pol->reject_unknown) | |
151 | goto err; | |
152 | print_unknown_handle = true; | |
153 | } | |
154 | ||
155 | k++; | |
156 | } | |
157 | p_out->num_perms = k; | |
158 | } | |
159 | ||
160 | if (print_unknown_handle) | |
b54c85c1 | 161 | pr_info("SELinux: the above unknown classes and permissions will be %s\n", |
c6d3aaa4 SS |
162 | pol->allow_unknown ? "allowed" : "denied"); |
163 | ||
aa8e712c | 164 | out_map->size = i; |
c6d3aaa4 SS |
165 | return 0; |
166 | err: | |
aa8e712c SS |
167 | kfree(out_map->mapping); |
168 | out_map->mapping = NULL; | |
c6d3aaa4 SS |
169 | return -EINVAL; |
170 | } | |
171 | ||
172 | /* | |
173 | * Get real, policy values from mapped values | |
174 | */ | |
175 | ||
aa8e712c | 176 | static u16 unmap_class(struct selinux_map *map, u16 tclass) |
c6d3aaa4 | 177 | { |
aa8e712c SS |
178 | if (tclass < map->size) |
179 | return map->mapping[tclass].value; | |
c6d3aaa4 SS |
180 | |
181 | return tclass; | |
182 | } | |
183 | ||
6f5317e7 HC |
184 | /* |
185 | * Get kernel value for class from its policy value | |
186 | */ | |
aa8e712c | 187 | static u16 map_class(struct selinux_map *map, u16 pol_value) |
6f5317e7 HC |
188 | { |
189 | u16 i; | |
190 | ||
aa8e712c SS |
191 | for (i = 1; i < map->size; i++) { |
192 | if (map->mapping[i].value == pol_value) | |
6f5317e7 HC |
193 | return i; |
194 | } | |
195 | ||
85cd6da5 | 196 | return SECCLASS_NULL; |
6f5317e7 HC |
197 | } |
198 | ||
aa8e712c SS |
199 | static void map_decision(struct selinux_map *map, |
200 | u16 tclass, struct av_decision *avd, | |
c6d3aaa4 SS |
201 | int allow_unknown) |
202 | { | |
aa8e712c SS |
203 | if (tclass < map->size) { |
204 | struct selinux_mapping *mapping = &map->mapping[tclass]; | |
205 | unsigned int i, n = mapping->num_perms; | |
c6d3aaa4 SS |
206 | u32 result; |
207 | ||
208 | for (i = 0, result = 0; i < n; i++) { | |
aa8e712c | 209 | if (avd->allowed & mapping->perms[i]) |
aa4b6051 | 210 | result |= (u32)1<<i; |
aa8e712c | 211 | if (allow_unknown && !mapping->perms[i]) |
aa4b6051 | 212 | result |= (u32)1<<i; |
c6d3aaa4 SS |
213 | } |
214 | avd->allowed = result; | |
215 | ||
216 | for (i = 0, result = 0; i < n; i++) | |
aa8e712c | 217 | if (avd->auditallow & mapping->perms[i]) |
aa4b6051 | 218 | result |= (u32)1<<i; |
c6d3aaa4 SS |
219 | avd->auditallow = result; |
220 | ||
221 | for (i = 0, result = 0; i < n; i++) { | |
aa8e712c | 222 | if (avd->auditdeny & mapping->perms[i]) |
aa4b6051 | 223 | result |= (u32)1<<i; |
aa8e712c | 224 | if (!allow_unknown && !mapping->perms[i]) |
aa4b6051 | 225 | result |= (u32)1<<i; |
c6d3aaa4 | 226 | } |
0bce9527 EP |
227 | /* |
228 | * In case the kernel has a bug and requests a permission | |
229 | * between num_perms and the maximum permission number, we | |
230 | * should audit that denial | |
231 | */ | |
232 | for (; i < (sizeof(u32)*8); i++) | |
aa4b6051 | 233 | result |= (u32)1<<i; |
c6d3aaa4 SS |
234 | avd->auditdeny = result; |
235 | } | |
236 | } | |
237 | ||
e67b7985 | 238 | int security_mls_enabled(void) |
0719aaf5 | 239 | { |
46169802 | 240 | int mls_enabled; |
1b8b31a2 | 241 | struct selinux_policy *policy; |
aa8e712c | 242 | |
e67b7985 | 243 | if (!selinux_initialized()) |
46169802 SS |
244 | return 0; |
245 | ||
1b8b31a2 | 246 | rcu_read_lock(); |
e67b7985 | 247 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
248 | mls_enabled = policy->policydb.mls_enabled; |
249 | rcu_read_unlock(); | |
46169802 | 250 | return mls_enabled; |
0719aaf5 | 251 | } |
c6d3aaa4 | 252 | |
1da177e4 LT |
253 | /* |
254 | * Return the boolean value of a constraint expression | |
255 | * when it is applied to the specified source and target | |
256 | * security contexts. | |
257 | * | |
258 | * xcontext is a special beast... It is used by the validatetrans rules | |
259 | * only. For these rules, scontext is the context before the transition, | |
260 | * tcontext is the context after the transition, and xcontext is the context | |
261 | * of the process performing the transition. All other callers of | |
262 | * constraint_expr_eval should pass in NULL for xcontext. | |
263 | */ | |
aa8e712c SS |
264 | static int constraint_expr_eval(struct policydb *policydb, |
265 | struct context *scontext, | |
1da177e4 LT |
266 | struct context *tcontext, |
267 | struct context *xcontext, | |
268 | struct constraint_expr *cexpr) | |
269 | { | |
270 | u32 val1, val2; | |
271 | struct context *c; | |
272 | struct role_datum *r1, *r2; | |
273 | struct mls_level *l1, *l2; | |
274 | struct constraint_expr *e; | |
275 | int s[CEXPR_MAXDEPTH]; | |
276 | int sp = -1; | |
277 | ||
278 | for (e = cexpr; e; e = e->next) { | |
279 | switch (e->expr_type) { | |
280 | case CEXPR_NOT: | |
281 | BUG_ON(sp < 0); | |
282 | s[sp] = !s[sp]; | |
283 | break; | |
284 | case CEXPR_AND: | |
285 | BUG_ON(sp < 1); | |
286 | sp--; | |
c1a7368a | 287 | s[sp] &= s[sp + 1]; |
1da177e4 LT |
288 | break; |
289 | case CEXPR_OR: | |
290 | BUG_ON(sp < 1); | |
291 | sp--; | |
c1a7368a | 292 | s[sp] |= s[sp + 1]; |
1da177e4 LT |
293 | break; |
294 | case CEXPR_ATTR: | |
c1a7368a | 295 | if (sp == (CEXPR_MAXDEPTH - 1)) |
1da177e4 LT |
296 | return 0; |
297 | switch (e->attr) { | |
298 | case CEXPR_USER: | |
299 | val1 = scontext->user; | |
300 | val2 = tcontext->user; | |
301 | break; | |
302 | case CEXPR_TYPE: | |
303 | val1 = scontext->type; | |
304 | val2 = tcontext->type; | |
305 | break; | |
306 | case CEXPR_ROLE: | |
307 | val1 = scontext->role; | |
308 | val2 = tcontext->role; | |
aa8e712c SS |
309 | r1 = policydb->role_val_to_struct[val1 - 1]; |
310 | r2 = policydb->role_val_to_struct[val2 - 1]; | |
1da177e4 LT |
311 | switch (e->op) { |
312 | case CEXPR_DOM: | |
313 | s[++sp] = ebitmap_get_bit(&r1->dominates, | |
314 | val2 - 1); | |
315 | continue; | |
316 | case CEXPR_DOMBY: | |
317 | s[++sp] = ebitmap_get_bit(&r2->dominates, | |
318 | val1 - 1); | |
319 | continue; | |
320 | case CEXPR_INCOMP: | |
5d55a345 EP |
321 | s[++sp] = (!ebitmap_get_bit(&r1->dominates, |
322 | val2 - 1) && | |
323 | !ebitmap_get_bit(&r2->dominates, | |
324 | val1 - 1)); | |
1da177e4 LT |
325 | continue; |
326 | default: | |
327 | break; | |
328 | } | |
329 | break; | |
330 | case CEXPR_L1L2: | |
331 | l1 = &(scontext->range.level[0]); | |
332 | l2 = &(tcontext->range.level[0]); | |
333 | goto mls_ops; | |
334 | case CEXPR_L1H2: | |
335 | l1 = &(scontext->range.level[0]); | |
336 | l2 = &(tcontext->range.level[1]); | |
337 | goto mls_ops; | |
338 | case CEXPR_H1L2: | |
339 | l1 = &(scontext->range.level[1]); | |
340 | l2 = &(tcontext->range.level[0]); | |
341 | goto mls_ops; | |
342 | case CEXPR_H1H2: | |
343 | l1 = &(scontext->range.level[1]); | |
344 | l2 = &(tcontext->range.level[1]); | |
345 | goto mls_ops; | |
346 | case CEXPR_L1H1: | |
347 | l1 = &(scontext->range.level[0]); | |
348 | l2 = &(scontext->range.level[1]); | |
349 | goto mls_ops; | |
350 | case CEXPR_L2H2: | |
351 | l1 = &(tcontext->range.level[0]); | |
352 | l2 = &(tcontext->range.level[1]); | |
353 | goto mls_ops; | |
354 | mls_ops: | |
a9029d97 CG |
355 | switch (e->op) { |
356 | case CEXPR_EQ: | |
357 | s[++sp] = mls_level_eq(l1, l2); | |
358 | continue; | |
359 | case CEXPR_NEQ: | |
360 | s[++sp] = !mls_level_eq(l1, l2); | |
361 | continue; | |
362 | case CEXPR_DOM: | |
363 | s[++sp] = mls_level_dom(l1, l2); | |
364 | continue; | |
365 | case CEXPR_DOMBY: | |
366 | s[++sp] = mls_level_dom(l2, l1); | |
367 | continue; | |
368 | case CEXPR_INCOMP: | |
369 | s[++sp] = mls_level_incomp(l2, l1); | |
370 | continue; | |
371 | default: | |
372 | BUG(); | |
373 | return 0; | |
374 | } | |
375 | break; | |
1da177e4 LT |
376 | default: |
377 | BUG(); | |
378 | return 0; | |
379 | } | |
380 | ||
381 | switch (e->op) { | |
382 | case CEXPR_EQ: | |
383 | s[++sp] = (val1 == val2); | |
384 | break; | |
385 | case CEXPR_NEQ: | |
386 | s[++sp] = (val1 != val2); | |
387 | break; | |
388 | default: | |
389 | BUG(); | |
390 | return 0; | |
391 | } | |
392 | break; | |
393 | case CEXPR_NAMES: | |
394 | if (sp == (CEXPR_MAXDEPTH-1)) | |
395 | return 0; | |
396 | c = scontext; | |
397 | if (e->attr & CEXPR_TARGET) | |
398 | c = tcontext; | |
399 | else if (e->attr & CEXPR_XTARGET) { | |
400 | c = xcontext; | |
401 | if (!c) { | |
402 | BUG(); | |
403 | return 0; | |
404 | } | |
405 | } | |
406 | if (e->attr & CEXPR_USER) | |
407 | val1 = c->user; | |
408 | else if (e->attr & CEXPR_ROLE) | |
409 | val1 = c->role; | |
410 | else if (e->attr & CEXPR_TYPE) | |
411 | val1 = c->type; | |
412 | else { | |
413 | BUG(); | |
414 | return 0; | |
415 | } | |
416 | ||
417 | switch (e->op) { | |
418 | case CEXPR_EQ: | |
419 | s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); | |
420 | break; | |
421 | case CEXPR_NEQ: | |
422 | s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); | |
423 | break; | |
424 | default: | |
425 | BUG(); | |
426 | return 0; | |
427 | } | |
428 | break; | |
429 | default: | |
430 | BUG(); | |
431 | return 0; | |
432 | } | |
433 | } | |
434 | ||
435 | BUG_ON(sp != 0); | |
436 | return s[0]; | |
437 | } | |
438 | ||
44c2d9bd KK |
439 | /* |
440 | * security_dump_masked_av - dumps masked permissions during | |
441 | * security_compute_av due to RBAC, MLS/Constraint and Type bounds. | |
442 | */ | |
443 | static int dump_masked_av_helper(void *k, void *d, void *args) | |
444 | { | |
445 | struct perm_datum *pdatum = d; | |
446 | char **permission_names = args; | |
447 | ||
448 | BUG_ON(pdatum->value < 1 || pdatum->value > 32); | |
449 | ||
450 | permission_names[pdatum->value - 1] = (char *)k; | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
aa8e712c SS |
455 | static void security_dump_masked_av(struct policydb *policydb, |
456 | struct context *scontext, | |
44c2d9bd KK |
457 | struct context *tcontext, |
458 | u16 tclass, | |
459 | u32 permissions, | |
460 | const char *reason) | |
461 | { | |
462 | struct common_datum *common_dat; | |
463 | struct class_datum *tclass_dat; | |
464 | struct audit_buffer *ab; | |
465 | char *tclass_name; | |
466 | char *scontext_name = NULL; | |
467 | char *tcontext_name = NULL; | |
468 | char *permission_names[32]; | |
2da5d31b JM |
469 | int index; |
470 | u32 length; | |
44c2d9bd KK |
471 | bool need_comma = false; |
472 | ||
473 | if (!permissions) | |
474 | return; | |
475 | ||
aa8e712c SS |
476 | tclass_name = sym_name(policydb, SYM_CLASSES, tclass - 1); |
477 | tclass_dat = policydb->class_val_to_struct[tclass - 1]; | |
44c2d9bd KK |
478 | common_dat = tclass_dat->comdatum; |
479 | ||
480 | /* init permission_names */ | |
481 | if (common_dat && | |
03414a49 | 482 | hashtab_map(&common_dat->permissions.table, |
44c2d9bd KK |
483 | dump_masked_av_helper, permission_names) < 0) |
484 | goto out; | |
485 | ||
03414a49 | 486 | if (hashtab_map(&tclass_dat->permissions.table, |
44c2d9bd KK |
487 | dump_masked_av_helper, permission_names) < 0) |
488 | goto out; | |
489 | ||
490 | /* get scontext/tcontext in text form */ | |
aa8e712c | 491 | if (context_struct_to_string(policydb, scontext, |
44c2d9bd KK |
492 | &scontext_name, &length) < 0) |
493 | goto out; | |
494 | ||
aa8e712c | 495 | if (context_struct_to_string(policydb, tcontext, |
44c2d9bd KK |
496 | &tcontext_name, &length) < 0) |
497 | goto out; | |
498 | ||
499 | /* audit a message */ | |
cdfb6b34 | 500 | ab = audit_log_start(audit_context(), |
44c2d9bd KK |
501 | GFP_ATOMIC, AUDIT_SELINUX_ERR); |
502 | if (!ab) | |
503 | goto out; | |
504 | ||
505 | audit_log_format(ab, "op=security_compute_av reason=%s " | |
506 | "scontext=%s tcontext=%s tclass=%s perms=", | |
507 | reason, scontext_name, tcontext_name, tclass_name); | |
508 | ||
509 | for (index = 0; index < 32; index++) { | |
510 | u32 mask = (1 << index); | |
511 | ||
512 | if ((mask & permissions) == 0) | |
513 | continue; | |
514 | ||
515 | audit_log_format(ab, "%s%s", | |
516 | need_comma ? "," : "", | |
517 | permission_names[index] | |
518 | ? permission_names[index] : "????"); | |
519 | need_comma = true; | |
520 | } | |
521 | audit_log_end(ab); | |
522 | out: | |
523 | /* release scontext/tcontext */ | |
524 | kfree(tcontext_name); | |
525 | kfree(scontext_name); | |
44c2d9bd KK |
526 | } |
527 | ||
d9250dea KK |
528 | /* |
529 | * security_boundary_permission - drops violated permissions | |
530 | * on boundary constraint. | |
531 | */ | |
aa8e712c SS |
532 | static void type_attribute_bounds_av(struct policydb *policydb, |
533 | struct context *scontext, | |
d9250dea KK |
534 | struct context *tcontext, |
535 | u16 tclass, | |
d9250dea KK |
536 | struct av_decision *avd) |
537 | { | |
2ae3ba39 | 538 | struct context lo_scontext; |
7ea59202 | 539 | struct context lo_tcontext, *tcontextp = tcontext; |
2ae3ba39 | 540 | struct av_decision lo_avd; |
23bdecb0 EP |
541 | struct type_datum *source; |
542 | struct type_datum *target; | |
2ae3ba39 | 543 | u32 masked = 0; |
d9250dea | 544 | |
f07ea1d4 | 545 | source = policydb->type_val_to_struct[scontext->type - 1]; |
23bdecb0 EP |
546 | BUG_ON(!source); |
547 | ||
7ea59202 SS |
548 | if (!source->bounds) |
549 | return; | |
550 | ||
f07ea1d4 | 551 | target = policydb->type_val_to_struct[tcontext->type - 1]; |
23bdecb0 EP |
552 | BUG_ON(!target); |
553 | ||
7ea59202 | 554 | memset(&lo_avd, 0, sizeof(lo_avd)); |
d9250dea | 555 | |
7ea59202 SS |
556 | memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); |
557 | lo_scontext.type = source->bounds; | |
2ae3ba39 KK |
558 | |
559 | if (target->bounds) { | |
2ae3ba39 KK |
560 | memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); |
561 | lo_tcontext.type = target->bounds; | |
7ea59202 | 562 | tcontextp = &lo_tcontext; |
2ae3ba39 KK |
563 | } |
564 | ||
aa8e712c | 565 | context_struct_compute_av(policydb, &lo_scontext, |
7ea59202 SS |
566 | tcontextp, |
567 | tclass, | |
568 | &lo_avd, | |
569 | NULL); | |
2ae3ba39 | 570 | |
7ea59202 | 571 | masked = ~lo_avd.allowed & avd->allowed; |
d9250dea | 572 | |
7ea59202 SS |
573 | if (likely(!masked)) |
574 | return; /* no masked permission */ | |
d9250dea | 575 | |
7ea59202 SS |
576 | /* mask violated permissions */ |
577 | avd->allowed &= ~masked; | |
578 | ||
579 | /* audit masked permissions */ | |
aa8e712c | 580 | security_dump_masked_av(policydb, scontext, tcontext, |
7ea59202 | 581 | tclass, masked, "bounds"); |
d9250dea KK |
582 | } |
583 | ||
1da177e4 | 584 | /* |
fa1aa143 | 585 | * flag which drivers have permissions |
3d9047a0 | 586 | * only looking for ioctl based extended permissions |
fa1aa143 JVS |
587 | */ |
588 | void services_compute_xperms_drivers( | |
589 | struct extended_perms *xperms, | |
590 | struct avtab_node *node) | |
591 | { | |
592 | unsigned int i; | |
593 | ||
594 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | |
595 | /* if one or more driver has all permissions allowed */ | |
596 | for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++) | |
597 | xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i]; | |
598 | } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | |
599 | /* if allowing permissions within a driver */ | |
600 | security_xperm_set(xperms->drivers.p, | |
601 | node->datum.u.xperms->driver); | |
602 | } | |
603 | ||
44141f58 | 604 | xperms->len = 1; |
fa1aa143 JVS |
605 | } |
606 | ||
607 | /* | |
608 | * Compute access vectors and extended permissions based on a context | |
609 | * structure pair for the permissions in a particular class. | |
1da177e4 | 610 | */ |
aa8e712c SS |
611 | static void context_struct_compute_av(struct policydb *policydb, |
612 | struct context *scontext, | |
613 | struct context *tcontext, | |
614 | u16 tclass, | |
615 | struct av_decision *avd, | |
616 | struct extended_perms *xperms) | |
1da177e4 LT |
617 | { |
618 | struct constraint_node *constraint; | |
619 | struct role_allow *ra; | |
620 | struct avtab_key avkey; | |
782ebb99 | 621 | struct avtab_node *node; |
1da177e4 | 622 | struct class_datum *tclass_datum; |
782ebb99 SS |
623 | struct ebitmap *sattr, *tattr; |
624 | struct ebitmap_node *snode, *tnode; | |
625 | unsigned int i, j; | |
1da177e4 | 626 | |
1da177e4 | 627 | avd->allowed = 0; |
1da177e4 LT |
628 | avd->auditallow = 0; |
629 | avd->auditdeny = 0xffffffff; | |
fa1aa143 JVS |
630 | if (xperms) { |
631 | memset(&xperms->drivers, 0, sizeof(xperms->drivers)); | |
632 | xperms->len = 0; | |
633 | } | |
1da177e4 | 634 | |
aa8e712c | 635 | if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { |
85154170 | 636 | pr_warn_ratelimited("SELinux: Invalid class %u\n", tclass); |
19439d05 | 637 | return; |
c6d3aaa4 | 638 | } |
3f12070e | 639 | |
aa8e712c | 640 | tclass_datum = policydb->class_val_to_struct[tclass - 1]; |
3f12070e | 641 | |
1da177e4 LT |
642 | /* |
643 | * If a specific type enforcement rule was defined for | |
644 | * this permission check, then use it. | |
645 | */ | |
1da177e4 | 646 | avkey.target_class = tclass; |
fa1aa143 | 647 | avkey.specified = AVTAB_AV | AVTAB_XPERMS; |
acdf52d9 | 648 | sattr = &policydb->type_attr_map_array[scontext->type - 1]; |
acdf52d9 | 649 | tattr = &policydb->type_attr_map_array[tcontext->type - 1]; |
9fe79ad1 KK |
650 | ebitmap_for_each_positive_bit(sattr, snode, i) { |
651 | ebitmap_for_each_positive_bit(tattr, tnode, j) { | |
782ebb99 SS |
652 | avkey.source_type = i + 1; |
653 | avkey.target_type = j + 1; | |
aa8e712c SS |
654 | for (node = avtab_search_node(&policydb->te_avtab, |
655 | &avkey); | |
dbc74c65 | 656 | node; |
782ebb99 SS |
657 | node = avtab_search_node_next(node, avkey.specified)) { |
658 | if (node->key.specified == AVTAB_ALLOWED) | |
fa1aa143 | 659 | avd->allowed |= node->datum.u.data; |
782ebb99 | 660 | else if (node->key.specified == AVTAB_AUDITALLOW) |
fa1aa143 | 661 | avd->auditallow |= node->datum.u.data; |
782ebb99 | 662 | else if (node->key.specified == AVTAB_AUDITDENY) |
fa1aa143 JVS |
663 | avd->auditdeny &= node->datum.u.data; |
664 | else if (xperms && (node->key.specified & AVTAB_XPERMS)) | |
665 | services_compute_xperms_drivers(xperms, node); | |
782ebb99 | 666 | } |
1da177e4 | 667 | |
782ebb99 | 668 | /* Check conditional av table for additional permissions */ |
aa8e712c | 669 | cond_compute_av(&policydb->te_cond_avtab, &avkey, |
fa1aa143 | 670 | avd, xperms); |
782ebb99 SS |
671 | |
672 | } | |
673 | } | |
1da177e4 LT |
674 | |
675 | /* | |
676 | * Remove any permissions prohibited by a constraint (this includes | |
677 | * the MLS policy). | |
678 | */ | |
679 | constraint = tclass_datum->constraints; | |
680 | while (constraint) { | |
681 | if ((constraint->permissions & (avd->allowed)) && | |
aa8e712c | 682 | !constraint_expr_eval(policydb, scontext, tcontext, NULL, |
1da177e4 | 683 | constraint->expr)) { |
caabbdc0 | 684 | avd->allowed &= ~(constraint->permissions); |
1da177e4 LT |
685 | } |
686 | constraint = constraint->next; | |
687 | } | |
688 | ||
689 | /* | |
690 | * If checking process transition permission and the | |
691 | * role is changing, then check the (current_role, new_role) | |
692 | * pair. | |
693 | */ | |
aa8e712c SS |
694 | if (tclass == policydb->process_class && |
695 | (avd->allowed & policydb->process_trans_perms) && | |
1da177e4 | 696 | scontext->role != tcontext->role) { |
aa8e712c | 697 | for (ra = policydb->role_allow; ra; ra = ra->next) { |
1da177e4 LT |
698 | if (scontext->role == ra->role && |
699 | tcontext->role == ra->new_role) | |
700 | break; | |
701 | } | |
702 | if (!ra) | |
aa8e712c | 703 | avd->allowed &= ~policydb->process_trans_perms; |
1da177e4 LT |
704 | } |
705 | ||
d9250dea KK |
706 | /* |
707 | * If the given source and target types have boundary | |
708 | * constraint, lazy checks have to mask any violated | |
709 | * permission and notice it to userspace via audit. | |
710 | */ | |
aa8e712c | 711 | type_attribute_bounds_av(policydb, scontext, tcontext, |
19439d05 | 712 | tclass, avd); |
1da177e4 LT |
713 | } |
714 | ||
e67b7985 | 715 | static int security_validtrans_handle_fail(struct selinux_policy *policy, |
1b8b31a2 SS |
716 | struct sidtab_entry *oentry, |
717 | struct sidtab_entry *nentry, | |
718 | struct sidtab_entry *tentry, | |
719 | u16 tclass) | |
1da177e4 | 720 | { |
1b8b31a2 SS |
721 | struct policydb *p = &policy->policydb; |
722 | struct sidtab *sidtab = policy->sidtab; | |
1da177e4 LT |
723 | char *o = NULL, *n = NULL, *t = NULL; |
724 | u32 olen, nlen, tlen; | |
725 | ||
d97bd23c | 726 | if (sidtab_entry_to_string(p, sidtab, oentry, &o, &olen)) |
1da177e4 | 727 | goto out; |
d97bd23c | 728 | if (sidtab_entry_to_string(p, sidtab, nentry, &n, &nlen)) |
1da177e4 | 729 | goto out; |
d97bd23c | 730 | if (sidtab_entry_to_string(p, sidtab, tentry, &t, &tlen)) |
1da177e4 | 731 | goto out; |
cdfb6b34 | 732 | audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, |
4093a844 | 733 | "op=security_validate_transition seresult=denied" |
5d55a345 | 734 | " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", |
aa8e712c | 735 | o, n, t, sym_name(p, SYM_CLASSES, tclass-1)); |
1da177e4 LT |
736 | out: |
737 | kfree(o); | |
738 | kfree(n); | |
739 | kfree(t); | |
740 | ||
e67b7985 | 741 | if (!enforcing_enabled()) |
1da177e4 LT |
742 | return 0; |
743 | return -EPERM; | |
744 | } | |
745 | ||
e67b7985 | 746 | static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, |
f9df6458 | 747 | u16 orig_tclass, bool user) |
1da177e4 | 748 | { |
1b8b31a2 | 749 | struct selinux_policy *policy; |
aa8e712c SS |
750 | struct policydb *policydb; |
751 | struct sidtab *sidtab; | |
d97bd23c OM |
752 | struct sidtab_entry *oentry; |
753 | struct sidtab_entry *nentry; | |
754 | struct sidtab_entry *tentry; | |
1da177e4 LT |
755 | struct class_datum *tclass_datum; |
756 | struct constraint_node *constraint; | |
c6d3aaa4 | 757 | u16 tclass; |
1da177e4 LT |
758 | int rc = 0; |
759 | ||
aa8e712c | 760 | |
e67b7985 | 761 | if (!selinux_initialized()) |
1da177e4 LT |
762 | return 0; |
763 | ||
1b8b31a2 | 764 | rcu_read_lock(); |
aa8e712c | 765 | |
e67b7985 | 766 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
767 | policydb = &policy->policydb; |
768 | sidtab = policy->sidtab; | |
1da177e4 | 769 | |
f9df6458 | 770 | if (!user) |
1b8b31a2 | 771 | tclass = unmap_class(&policy->map, orig_tclass); |
f9df6458 AP |
772 | else |
773 | tclass = orig_tclass; | |
c6d3aaa4 | 774 | |
aa8e712c | 775 | if (!tclass || tclass > policydb->p_classes.nprim) { |
1da177e4 LT |
776 | rc = -EINVAL; |
777 | goto out; | |
778 | } | |
aa8e712c | 779 | tclass_datum = policydb->class_val_to_struct[tclass - 1]; |
1da177e4 | 780 | |
d97bd23c OM |
781 | oentry = sidtab_search_entry(sidtab, oldsid); |
782 | if (!oentry) { | |
b54c85c1 | 783 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 784 | __func__, oldsid); |
1da177e4 LT |
785 | rc = -EINVAL; |
786 | goto out; | |
787 | } | |
788 | ||
d97bd23c OM |
789 | nentry = sidtab_search_entry(sidtab, newsid); |
790 | if (!nentry) { | |
b54c85c1 | 791 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 792 | __func__, newsid); |
1da177e4 LT |
793 | rc = -EINVAL; |
794 | goto out; | |
795 | } | |
796 | ||
d97bd23c OM |
797 | tentry = sidtab_search_entry(sidtab, tasksid); |
798 | if (!tentry) { | |
b54c85c1 | 799 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 800 | __func__, tasksid); |
1da177e4 LT |
801 | rc = -EINVAL; |
802 | goto out; | |
803 | } | |
804 | ||
805 | constraint = tclass_datum->validatetrans; | |
806 | while (constraint) { | |
d97bd23c OM |
807 | if (!constraint_expr_eval(policydb, &oentry->context, |
808 | &nentry->context, &tentry->context, | |
809 | constraint->expr)) { | |
f9df6458 AP |
810 | if (user) |
811 | rc = -EPERM; | |
812 | else | |
e67b7985 | 813 | rc = security_validtrans_handle_fail(policy, |
1b8b31a2 SS |
814 | oentry, |
815 | nentry, | |
816 | tentry, | |
817 | tclass); | |
1da177e4 LT |
818 | goto out; |
819 | } | |
820 | constraint = constraint->next; | |
821 | } | |
822 | ||
823 | out: | |
1b8b31a2 | 824 | rcu_read_unlock(); |
1da177e4 LT |
825 | return rc; |
826 | } | |
827 | ||
e67b7985 | 828 | int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, |
aa8e712c | 829 | u16 tclass) |
f9df6458 | 830 | { |
e67b7985 | 831 | return security_compute_validatetrans(oldsid, newsid, tasksid, |
aa8e712c | 832 | tclass, true); |
f9df6458 AP |
833 | } |
834 | ||
e67b7985 | 835 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, |
f9df6458 AP |
836 | u16 orig_tclass) |
837 | { | |
e67b7985 | 838 | return security_compute_validatetrans(oldsid, newsid, tasksid, |
aa8e712c | 839 | orig_tclass, false); |
f9df6458 AP |
840 | } |
841 | ||
d9250dea KK |
842 | /* |
843 | * security_bounded_transition - check whether the given | |
844 | * transition is directed to bounded, or not. | |
845 | * It returns 0, if @newsid is bounded by @oldsid. | |
846 | * Otherwise, it returns error code. | |
847 | * | |
848 | * @oldsid : current security identifier | |
849 | * @newsid : destinated security identifier | |
850 | */ | |
e67b7985 | 851 | int security_bounded_transition(u32 old_sid, u32 new_sid) |
d9250dea | 852 | { |
1b8b31a2 | 853 | struct selinux_policy *policy; |
aa8e712c SS |
854 | struct policydb *policydb; |
855 | struct sidtab *sidtab; | |
d97bd23c | 856 | struct sidtab_entry *old_entry, *new_entry; |
d9250dea | 857 | struct type_datum *type; |
c50e125d | 858 | u32 index; |
4b02b524 | 859 | int rc; |
d9250dea | 860 | |
e67b7985 | 861 | if (!selinux_initialized()) |
4b14752e PM |
862 | return 0; |
863 | ||
1b8b31a2 | 864 | rcu_read_lock(); |
e67b7985 | 865 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
866 | policydb = &policy->policydb; |
867 | sidtab = policy->sidtab; | |
d9250dea | 868 | |
4b02b524 | 869 | rc = -EINVAL; |
d97bd23c OM |
870 | old_entry = sidtab_search_entry(sidtab, old_sid); |
871 | if (!old_entry) { | |
b54c85c1 | 872 | pr_err("SELinux: %s: unrecognized SID %u\n", |
d9250dea KK |
873 | __func__, old_sid); |
874 | goto out; | |
875 | } | |
876 | ||
4b02b524 | 877 | rc = -EINVAL; |
d97bd23c OM |
878 | new_entry = sidtab_search_entry(sidtab, new_sid); |
879 | if (!new_entry) { | |
b54c85c1 | 880 | pr_err("SELinux: %s: unrecognized SID %u\n", |
d9250dea KK |
881 | __func__, new_sid); |
882 | goto out; | |
883 | } | |
884 | ||
4b02b524 | 885 | rc = 0; |
af901ca1 | 886 | /* type/domain unchanged */ |
d97bd23c | 887 | if (old_entry->context.type == new_entry->context.type) |
d9250dea | 888 | goto out; |
d9250dea | 889 | |
d97bd23c | 890 | index = new_entry->context.type; |
d9250dea | 891 | while (true) { |
f07ea1d4 | 892 | type = policydb->type_val_to_struct[index - 1]; |
d9250dea KK |
893 | BUG_ON(!type); |
894 | ||
895 | /* not bounded anymore */ | |
4b02b524 EP |
896 | rc = -EPERM; |
897 | if (!type->bounds) | |
d9250dea | 898 | break; |
d9250dea KK |
899 | |
900 | /* @newsid is bounded by @oldsid */ | |
4b02b524 | 901 | rc = 0; |
d97bd23c | 902 | if (type->bounds == old_entry->context.type) |
d9250dea | 903 | break; |
4b02b524 | 904 | |
d9250dea KK |
905 | index = type->bounds; |
906 | } | |
44c2d9bd KK |
907 | |
908 | if (rc) { | |
909 | char *old_name = NULL; | |
910 | char *new_name = NULL; | |
2da5d31b | 911 | u32 length; |
44c2d9bd | 912 | |
d97bd23c OM |
913 | if (!sidtab_entry_to_string(policydb, sidtab, old_entry, |
914 | &old_name, &length) && | |
915 | !sidtab_entry_to_string(policydb, sidtab, new_entry, | |
916 | &new_name, &length)) { | |
cdfb6b34 | 917 | audit_log(audit_context(), |
44c2d9bd KK |
918 | GFP_ATOMIC, AUDIT_SELINUX_ERR, |
919 | "op=security_bounded_transition " | |
4093a844 | 920 | "seresult=denied " |
44c2d9bd KK |
921 | "oldcontext=%s newcontext=%s", |
922 | old_name, new_name); | |
923 | } | |
924 | kfree(new_name); | |
925 | kfree(old_name); | |
926 | } | |
d9250dea | 927 | out: |
1b8b31a2 | 928 | rcu_read_unlock(); |
d9250dea KK |
929 | |
930 | return rc; | |
931 | } | |
932 | ||
1b8b31a2 | 933 | static void avd_init(struct selinux_policy *policy, struct av_decision *avd) |
c6d3aaa4 | 934 | { |
19439d05 SS |
935 | avd->allowed = 0; |
936 | avd->auditallow = 0; | |
937 | avd->auditdeny = 0xffffffff; | |
1b8b31a2 SS |
938 | if (policy) |
939 | avd->seqno = policy->latest_granting; | |
940 | else | |
941 | avd->seqno = 0; | |
19439d05 | 942 | avd->flags = 0; |
c6d3aaa4 SS |
943 | } |
944 | ||
fa1aa143 JVS |
945 | void services_compute_xperms_decision(struct extended_perms_decision *xpermd, |
946 | struct avtab_node *node) | |
947 | { | |
948 | unsigned int i; | |
949 | ||
950 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | |
951 | if (xpermd->driver != node->datum.u.xperms->driver) | |
952 | return; | |
953 | } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | |
954 | if (!security_xperm_test(node->datum.u.xperms->perms.p, | |
955 | xpermd->driver)) | |
956 | return; | |
957 | } else { | |
958 | BUG(); | |
959 | } | |
960 | ||
961 | if (node->key.specified == AVTAB_XPERMS_ALLOWED) { | |
962 | xpermd->used |= XPERMS_ALLOWED; | |
963 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | |
964 | memset(xpermd->allowed->p, 0xff, | |
965 | sizeof(xpermd->allowed->p)); | |
966 | } | |
967 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | |
968 | for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++) | |
969 | xpermd->allowed->p[i] |= | |
970 | node->datum.u.xperms->perms.p[i]; | |
971 | } | |
972 | } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) { | |
973 | xpermd->used |= XPERMS_AUDITALLOW; | |
974 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | |
975 | memset(xpermd->auditallow->p, 0xff, | |
976 | sizeof(xpermd->auditallow->p)); | |
977 | } | |
978 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | |
979 | for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++) | |
980 | xpermd->auditallow->p[i] |= | |
981 | node->datum.u.xperms->perms.p[i]; | |
982 | } | |
983 | } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) { | |
984 | xpermd->used |= XPERMS_DONTAUDIT; | |
985 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | |
986 | memset(xpermd->dontaudit->p, 0xff, | |
987 | sizeof(xpermd->dontaudit->p)); | |
988 | } | |
989 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | |
990 | for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++) | |
991 | xpermd->dontaudit->p[i] |= | |
992 | node->datum.u.xperms->perms.p[i]; | |
993 | } | |
994 | } else { | |
995 | BUG(); | |
996 | } | |
997 | } | |
998 | ||
e67b7985 | 999 | void security_compute_xperms_decision(u32 ssid, |
aa8e712c SS |
1000 | u32 tsid, |
1001 | u16 orig_tclass, | |
1002 | u8 driver, | |
1003 | struct extended_perms_decision *xpermd) | |
fa1aa143 | 1004 | { |
1b8b31a2 | 1005 | struct selinux_policy *policy; |
aa8e712c SS |
1006 | struct policydb *policydb; |
1007 | struct sidtab *sidtab; | |
fa1aa143 JVS |
1008 | u16 tclass; |
1009 | struct context *scontext, *tcontext; | |
1010 | struct avtab_key avkey; | |
1011 | struct avtab_node *node; | |
1012 | struct ebitmap *sattr, *tattr; | |
1013 | struct ebitmap_node *snode, *tnode; | |
1014 | unsigned int i, j; | |
1015 | ||
1016 | xpermd->driver = driver; | |
1017 | xpermd->used = 0; | |
1018 | memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p)); | |
1019 | memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p)); | |
1020 | memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p)); | |
1021 | ||
1b8b31a2 | 1022 | rcu_read_lock(); |
e67b7985 | 1023 | if (!selinux_initialized()) |
fa1aa143 JVS |
1024 | goto allow; |
1025 | ||
e67b7985 | 1026 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
1027 | policydb = &policy->policydb; |
1028 | sidtab = policy->sidtab; | |
aa8e712c SS |
1029 | |
1030 | scontext = sidtab_search(sidtab, ssid); | |
fa1aa143 | 1031 | if (!scontext) { |
b54c85c1 | 1032 | pr_err("SELinux: %s: unrecognized SID %d\n", |
fa1aa143 JVS |
1033 | __func__, ssid); |
1034 | goto out; | |
1035 | } | |
1036 | ||
aa8e712c | 1037 | tcontext = sidtab_search(sidtab, tsid); |
fa1aa143 | 1038 | if (!tcontext) { |
b54c85c1 | 1039 | pr_err("SELinux: %s: unrecognized SID %d\n", |
fa1aa143 JVS |
1040 | __func__, tsid); |
1041 | goto out; | |
1042 | } | |
1043 | ||
1b8b31a2 | 1044 | tclass = unmap_class(&policy->map, orig_tclass); |
fa1aa143 | 1045 | if (unlikely(orig_tclass && !tclass)) { |
aa8e712c | 1046 | if (policydb->allow_unknown) |
fa1aa143 JVS |
1047 | goto allow; |
1048 | goto out; | |
1049 | } | |
1050 | ||
1051 | ||
aa8e712c | 1052 | if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { |
fa1aa143 JVS |
1053 | pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass); |
1054 | goto out; | |
1055 | } | |
1056 | ||
1057 | avkey.target_class = tclass; | |
1058 | avkey.specified = AVTAB_XPERMS; | |
acdf52d9 | 1059 | sattr = &policydb->type_attr_map_array[scontext->type - 1]; |
acdf52d9 | 1060 | tattr = &policydb->type_attr_map_array[tcontext->type - 1]; |
fa1aa143 JVS |
1061 | ebitmap_for_each_positive_bit(sattr, snode, i) { |
1062 | ebitmap_for_each_positive_bit(tattr, tnode, j) { | |
1063 | avkey.source_type = i + 1; | |
1064 | avkey.target_type = j + 1; | |
aa8e712c SS |
1065 | for (node = avtab_search_node(&policydb->te_avtab, |
1066 | &avkey); | |
fa1aa143 JVS |
1067 | node; |
1068 | node = avtab_search_node_next(node, avkey.specified)) | |
1069 | services_compute_xperms_decision(xpermd, node); | |
1070 | ||
aa8e712c | 1071 | cond_compute_xperms(&policydb->te_cond_avtab, |
fa1aa143 JVS |
1072 | &avkey, xpermd); |
1073 | } | |
1074 | } | |
1075 | out: | |
1b8b31a2 | 1076 | rcu_read_unlock(); |
fa1aa143 JVS |
1077 | return; |
1078 | allow: | |
1079 | memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p)); | |
1080 | goto out; | |
1081 | } | |
19439d05 | 1082 | |
1da177e4 LT |
1083 | /** |
1084 | * security_compute_av - Compute access vector decisions. | |
1085 | * @ssid: source security identifier | |
1086 | * @tsid: target security identifier | |
e9fd7292 | 1087 | * @orig_tclass: target security class |
1da177e4 | 1088 | * @avd: access vector decisions |
fa1aa143 | 1089 | * @xperms: extended permissions |
1da177e4 LT |
1090 | * |
1091 | * Compute a set of access vector decisions based on the | |
1092 | * SID pair (@ssid, @tsid) for the permissions in @tclass. | |
1da177e4 | 1093 | */ |
e67b7985 | 1094 | void security_compute_av(u32 ssid, |
19439d05 SS |
1095 | u32 tsid, |
1096 | u16 orig_tclass, | |
fa1aa143 JVS |
1097 | struct av_decision *avd, |
1098 | struct extended_perms *xperms) | |
1da177e4 | 1099 | { |
1b8b31a2 | 1100 | struct selinux_policy *policy; |
aa8e712c SS |
1101 | struct policydb *policydb; |
1102 | struct sidtab *sidtab; | |
c6d3aaa4 | 1103 | u16 tclass; |
19439d05 | 1104 | struct context *scontext = NULL, *tcontext = NULL; |
c6d3aaa4 | 1105 | |
1b8b31a2 | 1106 | rcu_read_lock(); |
e67b7985 | 1107 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 | 1108 | avd_init(policy, avd); |
fa1aa143 | 1109 | xperms->len = 0; |
e67b7985 | 1110 | if (!selinux_initialized()) |
c6d3aaa4 SS |
1111 | goto allow; |
1112 | ||
1b8b31a2 SS |
1113 | policydb = &policy->policydb; |
1114 | sidtab = policy->sidtab; | |
aa8e712c SS |
1115 | |
1116 | scontext = sidtab_search(sidtab, ssid); | |
19439d05 | 1117 | if (!scontext) { |
b54c85c1 | 1118 | pr_err("SELinux: %s: unrecognized SID %d\n", |
19439d05 SS |
1119 | __func__, ssid); |
1120 | goto out; | |
1121 | } | |
1122 | ||
1123 | /* permissive domain? */ | |
aa8e712c | 1124 | if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) |
19439d05 SS |
1125 | avd->flags |= AVD_FLAGS_PERMISSIVE; |
1126 | ||
aa8e712c | 1127 | tcontext = sidtab_search(sidtab, tsid); |
19439d05 | 1128 | if (!tcontext) { |
b54c85c1 | 1129 | pr_err("SELinux: %s: unrecognized SID %d\n", |
19439d05 SS |
1130 | __func__, tsid); |
1131 | goto out; | |
1132 | } | |
1133 | ||
1b8b31a2 | 1134 | tclass = unmap_class(&policy->map, orig_tclass); |
c6d3aaa4 | 1135 | if (unlikely(orig_tclass && !tclass)) { |
aa8e712c | 1136 | if (policydb->allow_unknown) |
c6d3aaa4 | 1137 | goto allow; |
b7f3008a | 1138 | goto out; |
c6d3aaa4 | 1139 | } |
aa8e712c SS |
1140 | context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, |
1141 | xperms); | |
1b8b31a2 | 1142 | map_decision(&policy->map, orig_tclass, avd, |
aa8e712c | 1143 | policydb->allow_unknown); |
b7f3008a | 1144 | out: |
1b8b31a2 | 1145 | rcu_read_unlock(); |
19439d05 | 1146 | return; |
c6d3aaa4 SS |
1147 | allow: |
1148 | avd->allowed = 0xffffffff; | |
b7f3008a | 1149 | goto out; |
c6d3aaa4 SS |
1150 | } |
1151 | ||
e67b7985 | 1152 | void security_compute_av_user(u32 ssid, |
19439d05 SS |
1153 | u32 tsid, |
1154 | u16 tclass, | |
1155 | struct av_decision *avd) | |
c6d3aaa4 | 1156 | { |
1b8b31a2 | 1157 | struct selinux_policy *policy; |
aa8e712c SS |
1158 | struct policydb *policydb; |
1159 | struct sidtab *sidtab; | |
19439d05 | 1160 | struct context *scontext = NULL, *tcontext = NULL; |
1da177e4 | 1161 | |
1b8b31a2 | 1162 | rcu_read_lock(); |
e67b7985 | 1163 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 | 1164 | avd_init(policy, avd); |
e67b7985 | 1165 | if (!selinux_initialized()) |
19439d05 SS |
1166 | goto allow; |
1167 | ||
1b8b31a2 SS |
1168 | policydb = &policy->policydb; |
1169 | sidtab = policy->sidtab; | |
aa8e712c SS |
1170 | |
1171 | scontext = sidtab_search(sidtab, ssid); | |
19439d05 | 1172 | if (!scontext) { |
b54c85c1 | 1173 | pr_err("SELinux: %s: unrecognized SID %d\n", |
19439d05 SS |
1174 | __func__, ssid); |
1175 | goto out; | |
1da177e4 LT |
1176 | } |
1177 | ||
19439d05 | 1178 | /* permissive domain? */ |
aa8e712c | 1179 | if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) |
19439d05 SS |
1180 | avd->flags |= AVD_FLAGS_PERMISSIVE; |
1181 | ||
aa8e712c | 1182 | tcontext = sidtab_search(sidtab, tsid); |
19439d05 | 1183 | if (!tcontext) { |
b54c85c1 | 1184 | pr_err("SELinux: %s: unrecognized SID %d\n", |
19439d05 SS |
1185 | __func__, tsid); |
1186 | goto out; | |
1187 | } | |
1188 | ||
1189 | if (unlikely(!tclass)) { | |
aa8e712c | 1190 | if (policydb->allow_unknown) |
19439d05 SS |
1191 | goto allow; |
1192 | goto out; | |
1193 | } | |
1194 | ||
aa8e712c SS |
1195 | context_struct_compute_av(policydb, scontext, tcontext, tclass, avd, |
1196 | NULL); | |
19439d05 | 1197 | out: |
1b8b31a2 | 1198 | rcu_read_unlock(); |
19439d05 SS |
1199 | return; |
1200 | allow: | |
1201 | avd->allowed = 0xffffffff; | |
1202 | goto out; | |
1da177e4 LT |
1203 | } |
1204 | ||
1205 | /* | |
1206 | * Write the security context string representation of | |
1207 | * the context structure `context' into a dynamically | |
1208 | * allocated string of the correct size. Set `*scontext' | |
1209 | * to point to this string and set `*scontext_len' to | |
1210 | * the length of the string. | |
1211 | */ | |
aa8e712c SS |
1212 | static int context_struct_to_string(struct policydb *p, |
1213 | struct context *context, | |
1214 | char **scontext, u32 *scontext_len) | |
1da177e4 LT |
1215 | { |
1216 | char *scontextp; | |
1217 | ||
d5630b9d EP |
1218 | if (scontext) |
1219 | *scontext = NULL; | |
1da177e4 LT |
1220 | *scontext_len = 0; |
1221 | ||
12b29f34 SS |
1222 | if (context->len) { |
1223 | *scontext_len = context->len; | |
bb7081ab EP |
1224 | if (scontext) { |
1225 | *scontext = kstrdup(context->str, GFP_ATOMIC); | |
1226 | if (!(*scontext)) | |
1227 | return -ENOMEM; | |
1228 | } | |
12b29f34 SS |
1229 | return 0; |
1230 | } | |
1231 | ||
1da177e4 | 1232 | /* Compute the size of the context. */ |
aa8e712c SS |
1233 | *scontext_len += strlen(sym_name(p, SYM_USERS, context->user - 1)) + 1; |
1234 | *scontext_len += strlen(sym_name(p, SYM_ROLES, context->role - 1)) + 1; | |
1235 | *scontext_len += strlen(sym_name(p, SYM_TYPES, context->type - 1)) + 1; | |
1236 | *scontext_len += mls_compute_context_len(p, context); | |
1da177e4 | 1237 | |
d5630b9d EP |
1238 | if (!scontext) |
1239 | return 0; | |
1240 | ||
1da177e4 LT |
1241 | /* Allocate space for the context; caller must free this space. */ |
1242 | scontextp = kmalloc(*scontext_len, GFP_ATOMIC); | |
5d55a345 | 1243 | if (!scontextp) |
1da177e4 | 1244 | return -ENOMEM; |
1da177e4 LT |
1245 | *scontext = scontextp; |
1246 | ||
1247 | /* | |
1248 | * Copy the user name, role name and type name into the context. | |
1249 | */ | |
9529c788 | 1250 | scontextp += sprintf(scontextp, "%s:%s:%s", |
aa8e712c SS |
1251 | sym_name(p, SYM_USERS, context->user - 1), |
1252 | sym_name(p, SYM_ROLES, context->role - 1), | |
1253 | sym_name(p, SYM_TYPES, context->type - 1)); | |
1da177e4 | 1254 | |
aa8e712c | 1255 | mls_sid_to_context(p, context, &scontextp); |
1da177e4 LT |
1256 | |
1257 | *scontextp = 0; | |
1258 | ||
1259 | return 0; | |
1260 | } | |
1261 | ||
d97bd23c OM |
1262 | static int sidtab_entry_to_string(struct policydb *p, |
1263 | struct sidtab *sidtab, | |
1264 | struct sidtab_entry *entry, | |
1265 | char **scontext, u32 *scontext_len) | |
1266 | { | |
1267 | int rc = sidtab_sid2str_get(sidtab, entry, scontext, scontext_len); | |
1268 | ||
1269 | if (rc != -ENOENT) | |
1270 | return rc; | |
1271 | ||
1272 | rc = context_struct_to_string(p, &entry->context, scontext, | |
1273 | scontext_len); | |
1274 | if (!rc && scontext) | |
1275 | sidtab_sid2str_put(sidtab, entry, *scontext, *scontext_len); | |
1276 | return rc; | |
1277 | } | |
1278 | ||
1da177e4 LT |
1279 | #include "initial_sid_to_string.h" |
1280 | ||
e67b7985 | 1281 | int security_sidtab_hash_stats(char *page) |
66f8e2f0 | 1282 | { |
1b8b31a2 | 1283 | struct selinux_policy *policy; |
66f8e2f0 JVS |
1284 | int rc; |
1285 | ||
e67b7985 | 1286 | if (!selinux_initialized()) { |
15b590a8 PM |
1287 | pr_err("SELinux: %s: called before initial load_policy\n", |
1288 | __func__); | |
1289 | return -EINVAL; | |
1290 | } | |
1291 | ||
1b8b31a2 | 1292 | rcu_read_lock(); |
e67b7985 | 1293 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
1294 | rc = sidtab_hash_stats(policy->sidtab, page); |
1295 | rcu_read_unlock(); | |
66f8e2f0 JVS |
1296 | |
1297 | return rc; | |
1298 | } | |
1299 | ||
f0ee2e46 JC |
1300 | const char *security_get_initial_sid_context(u32 sid) |
1301 | { | |
1302 | if (unlikely(sid > SECINITSID_NUM)) | |
1303 | return NULL; | |
1304 | return initial_sid_to_string[sid]; | |
1305 | } | |
1306 | ||
e67b7985 | 1307 | static int security_sid_to_context_core(u32 sid, char **scontext, |
fede1483 OM |
1308 | u32 *scontext_len, int force, |
1309 | int only_invalid) | |
1da177e4 | 1310 | { |
1b8b31a2 | 1311 | struct selinux_policy *policy; |
aa8e712c SS |
1312 | struct policydb *policydb; |
1313 | struct sidtab *sidtab; | |
d97bd23c | 1314 | struct sidtab_entry *entry; |
1da177e4 LT |
1315 | int rc = 0; |
1316 | ||
d5630b9d EP |
1317 | if (scontext) |
1318 | *scontext = NULL; | |
4f4acf3a SS |
1319 | *scontext_len = 0; |
1320 | ||
e67b7985 | 1321 | if (!selinux_initialized()) { |
1da177e4 LT |
1322 | if (sid <= SECINITSID_NUM) { |
1323 | char *scontextp; | |
ae254858 OM |
1324 | const char *s; |
1325 | ||
1326 | /* | |
1327 | * Before the policy is loaded, translate | |
1328 | * SECINITSID_INIT to "kernel", because systemd and | |
1329 | * libselinux < 2.6 take a getcon_raw() result that is | |
1330 | * both non-null and not "kernel" to mean that a policy | |
1331 | * is already loaded. | |
1332 | */ | |
1333 | if (sid == SECINITSID_INIT) | |
1334 | sid = SECINITSID_KERNEL; | |
1335 | ||
1336 | s = initial_sid_to_string[sid]; | |
e3e0b582 SS |
1337 | if (!s) |
1338 | return -EINVAL; | |
1339 | *scontext_len = strlen(s) + 1; | |
d5630b9d | 1340 | if (!scontext) |
e3e0b582 SS |
1341 | return 0; |
1342 | scontextp = kmemdup(s, *scontext_len, GFP_ATOMIC); | |
1343 | if (!scontextp) | |
1344 | return -ENOMEM; | |
1da177e4 | 1345 | *scontext = scontextp; |
e3e0b582 | 1346 | return 0; |
1da177e4 | 1347 | } |
b54c85c1 | 1348 | pr_err("SELinux: %s: called before initial " |
744ba35e | 1349 | "load_policy on unknown SID %d\n", __func__, sid); |
e3e0b582 | 1350 | return -EINVAL; |
1da177e4 | 1351 | } |
1b8b31a2 | 1352 | rcu_read_lock(); |
e67b7985 | 1353 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
1354 | policydb = &policy->policydb; |
1355 | sidtab = policy->sidtab; | |
d97bd23c | 1356 | |
12b29f34 | 1357 | if (force) |
d97bd23c | 1358 | entry = sidtab_search_entry_force(sidtab, sid); |
12b29f34 | 1359 | else |
d97bd23c OM |
1360 | entry = sidtab_search_entry(sidtab, sid); |
1361 | if (!entry) { | |
b54c85c1 | 1362 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 1363 | __func__, sid); |
1da177e4 LT |
1364 | rc = -EINVAL; |
1365 | goto out_unlock; | |
1366 | } | |
d97bd23c OM |
1367 | if (only_invalid && !entry->context.len) |
1368 | goto out_unlock; | |
1369 | ||
1370 | rc = sidtab_entry_to_string(policydb, sidtab, entry, scontext, | |
1371 | scontext_len); | |
1372 | ||
1da177e4 | 1373 | out_unlock: |
1b8b31a2 | 1374 | rcu_read_unlock(); |
1da177e4 LT |
1375 | return rc; |
1376 | ||
1377 | } | |
1378 | ||
12b29f34 SS |
1379 | /** |
1380 | * security_sid_to_context - Obtain a context for a given SID. | |
1381 | * @sid: security identifier, SID | |
1382 | * @scontext: security context | |
1383 | * @scontext_len: length in bytes | |
1384 | * | |
1385 | * Write the string representation of the context associated with @sid | |
1386 | * into a dynamically allocated string of the correct size. Set @scontext | |
1387 | * to point to this string and set @scontext_len to the length of the string. | |
1388 | */ | |
e67b7985 | 1389 | int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) |
1da177e4 | 1390 | { |
e67b7985 | 1391 | return security_sid_to_context_core(sid, scontext, |
fede1483 | 1392 | scontext_len, 0, 0); |
12b29f34 SS |
1393 | } |
1394 | ||
e67b7985 | 1395 | int security_sid_to_context_force(u32 sid, |
aa8e712c | 1396 | char **scontext, u32 *scontext_len) |
12b29f34 | 1397 | { |
e67b7985 | 1398 | return security_sid_to_context_core(sid, scontext, |
fede1483 OM |
1399 | scontext_len, 1, 0); |
1400 | } | |
1401 | ||
1402 | /** | |
1403 | * security_sid_to_context_inval - Obtain a context for a given SID if it | |
1404 | * is invalid. | |
1405 | * @sid: security identifier, SID | |
1406 | * @scontext: security context | |
1407 | * @scontext_len: length in bytes | |
1408 | * | |
1409 | * Write the string representation of the context associated with @sid | |
1410 | * into a dynamically allocated string of the correct size, but only if the | |
1411 | * context is invalid in the current policy. Set @scontext to point to | |
1412 | * this string (or NULL if the context is valid) and set @scontext_len to | |
1413 | * the length of the string (or 0 if the context is valid). | |
1414 | */ | |
e67b7985 | 1415 | int security_sid_to_context_inval(u32 sid, |
fede1483 OM |
1416 | char **scontext, u32 *scontext_len) |
1417 | { | |
e67b7985 | 1418 | return security_sid_to_context_core(sid, scontext, |
fede1483 | 1419 | scontext_len, 1, 1); |
12b29f34 SS |
1420 | } |
1421 | ||
9a59daa0 SS |
1422 | /* |
1423 | * Caveat: Mutates scontext. | |
1424 | */ | |
12b29f34 SS |
1425 | static int string_to_context_struct(struct policydb *pol, |
1426 | struct sidtab *sidtabp, | |
9a59daa0 | 1427 | char *scontext, |
12b29f34 | 1428 | struct context *ctx, |
9a59daa0 | 1429 | u32 def_sid) |
12b29f34 | 1430 | { |
1da177e4 LT |
1431 | struct role_datum *role; |
1432 | struct type_datum *typdatum; | |
1433 | struct user_datum *usrdatum; | |
1434 | char *scontextp, *p, oldc; | |
1435 | int rc = 0; | |
1436 | ||
12b29f34 | 1437 | context_init(ctx); |
1da177e4 | 1438 | |
1da177e4 LT |
1439 | /* Parse the security context. */ |
1440 | ||
1441 | rc = -EINVAL; | |
0b3c2b3d | 1442 | scontextp = scontext; |
1da177e4 LT |
1443 | |
1444 | /* Extract the user. */ | |
1445 | p = scontextp; | |
1446 | while (*p && *p != ':') | |
1447 | p++; | |
1448 | ||
1449 | if (*p == 0) | |
12b29f34 | 1450 | goto out; |
1da177e4 LT |
1451 | |
1452 | *p++ = 0; | |
1453 | ||
237389e3 | 1454 | usrdatum = symtab_search(&pol->p_users, scontextp); |
1da177e4 | 1455 | if (!usrdatum) |
12b29f34 | 1456 | goto out; |
1da177e4 | 1457 | |
12b29f34 | 1458 | ctx->user = usrdatum->value; |
1da177e4 LT |
1459 | |
1460 | /* Extract role. */ | |
1461 | scontextp = p; | |
1462 | while (*p && *p != ':') | |
1463 | p++; | |
1464 | ||
1465 | if (*p == 0) | |
12b29f34 | 1466 | goto out; |
1da177e4 LT |
1467 | |
1468 | *p++ = 0; | |
1469 | ||
237389e3 | 1470 | role = symtab_search(&pol->p_roles, scontextp); |
1da177e4 | 1471 | if (!role) |
12b29f34 SS |
1472 | goto out; |
1473 | ctx->role = role->value; | |
1da177e4 LT |
1474 | |
1475 | /* Extract type. */ | |
1476 | scontextp = p; | |
1477 | while (*p && *p != ':') | |
1478 | p++; | |
1479 | oldc = *p; | |
1480 | *p++ = 0; | |
1481 | ||
237389e3 | 1482 | typdatum = symtab_search(&pol->p_types, scontextp); |
d9250dea | 1483 | if (!typdatum || typdatum->attribute) |
12b29f34 | 1484 | goto out; |
1da177e4 | 1485 | |
12b29f34 | 1486 | ctx->type = typdatum->value; |
1da177e4 | 1487 | |
95ffe194 | 1488 | rc = mls_context_to_sid(pol, oldc, p, ctx, sidtabp, def_sid); |
1da177e4 | 1489 | if (rc) |
12b29f34 | 1490 | goto out; |
1da177e4 | 1491 | |
1da177e4 | 1492 | /* Check the validity of the new context. */ |
95ffe194 | 1493 | rc = -EINVAL; |
4b02b524 | 1494 | if (!policydb_context_isvalid(pol, ctx)) |
12b29f34 | 1495 | goto out; |
12b29f34 SS |
1496 | rc = 0; |
1497 | out: | |
8e531af9 EP |
1498 | if (rc) |
1499 | context_destroy(ctx); | |
12b29f34 SS |
1500 | return rc; |
1501 | } | |
1502 | ||
e67b7985 | 1503 | static int security_context_to_sid_core(const char *scontext, u32 scontext_len, |
12b29f34 SS |
1504 | u32 *sid, u32 def_sid, gfp_t gfp_flags, |
1505 | int force) | |
1506 | { | |
1b8b31a2 | 1507 | struct selinux_policy *policy; |
aa8e712c SS |
1508 | struct policydb *policydb; |
1509 | struct sidtab *sidtab; | |
9a59daa0 | 1510 | char *scontext2, *str = NULL; |
12b29f34 SS |
1511 | struct context context; |
1512 | int rc = 0; | |
1513 | ||
2172fa70 SS |
1514 | /* An empty security context is never valid. */ |
1515 | if (!scontext_len) | |
1516 | return -EINVAL; | |
1517 | ||
ef28df55 PM |
1518 | /* Copy the string to allow changes and ensure a NUL terminator */ |
1519 | scontext2 = kmemdup_nul(scontext, scontext_len, gfp_flags); | |
1520 | if (!scontext2) | |
1521 | return -ENOMEM; | |
1522 | ||
e67b7985 | 1523 | if (!selinux_initialized()) { |
c50e125d | 1524 | u32 i; |
12b29f34 SS |
1525 | |
1526 | for (i = 1; i < SECINITSID_NUM; i++) { | |
e3e0b582 SS |
1527 | const char *s = initial_sid_to_string[i]; |
1528 | ||
1529 | if (s && !strcmp(s, scontext2)) { | |
12b29f34 | 1530 | *sid = i; |
ef28df55 | 1531 | goto out; |
12b29f34 SS |
1532 | } |
1533 | } | |
1534 | *sid = SECINITSID_KERNEL; | |
ef28df55 | 1535 | goto out; |
12b29f34 SS |
1536 | } |
1537 | *sid = SECSID_NULL; | |
1538 | ||
9a59daa0 SS |
1539 | if (force) { |
1540 | /* Save another copy for storing in uninterpreted form */ | |
4b02b524 | 1541 | rc = -ENOMEM; |
9a59daa0 | 1542 | str = kstrdup(scontext2, gfp_flags); |
4b02b524 EP |
1543 | if (!str) |
1544 | goto out; | |
9a59daa0 | 1545 | } |
9ad6e9cb | 1546 | retry: |
1b8b31a2 | 1547 | rcu_read_lock(); |
e67b7985 | 1548 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
1549 | policydb = &policy->policydb; |
1550 | sidtab = policy->sidtab; | |
aa8e712c | 1551 | rc = string_to_context_struct(policydb, sidtab, scontext2, |
95ffe194 | 1552 | &context, def_sid); |
12b29f34 | 1553 | if (rc == -EINVAL && force) { |
9a59daa0 | 1554 | context.str = str; |
efe3de79 | 1555 | context.len = strlen(str) + 1; |
9a59daa0 | 1556 | str = NULL; |
12b29f34 | 1557 | } else if (rc) |
4b02b524 | 1558 | goto out_unlock; |
225621c9 | 1559 | rc = sidtab_context_to_sid(sidtab, &context, sid); |
9ad6e9cb OM |
1560 | if (rc == -ESTALE) { |
1561 | rcu_read_unlock(); | |
1562 | if (context.str) { | |
1563 | str = context.str; | |
1564 | context.str = NULL; | |
1565 | } | |
1566 | context_destroy(&context); | |
1567 | goto retry; | |
1568 | } | |
8e531af9 | 1569 | context_destroy(&context); |
4b02b524 | 1570 | out_unlock: |
1b8b31a2 | 1571 | rcu_read_unlock(); |
4b02b524 | 1572 | out: |
9a59daa0 SS |
1573 | kfree(scontext2); |
1574 | kfree(str); | |
1da177e4 LT |
1575 | return rc; |
1576 | } | |
1577 | ||
f5c1d5b2 JM |
1578 | /** |
1579 | * security_context_to_sid - Obtain a SID for a given security context. | |
1580 | * @scontext: security context | |
1581 | * @scontext_len: length in bytes | |
1582 | * @sid: security identifier, SID | |
52a4c640 | 1583 | * @gfp: context for the allocation |
f5c1d5b2 JM |
1584 | * |
1585 | * Obtains a SID associated with the security context that | |
1586 | * has the string representation specified by @scontext. | |
1587 | * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient | |
1588 | * memory is available, or 0 on success. | |
1589 | */ | |
e67b7985 | 1590 | int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, |
52a4c640 | 1591 | gfp_t gfp) |
f5c1d5b2 | 1592 | { |
e67b7985 | 1593 | return security_context_to_sid_core(scontext, scontext_len, |
52a4c640 | 1594 | sid, SECSID_NULL, gfp, 0); |
44be2f65 RV |
1595 | } |
1596 | ||
e67b7985 | 1597 | int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp) |
44be2f65 | 1598 | { |
e67b7985 | 1599 | return security_context_to_sid(scontext, strlen(scontext), |
aa8e712c | 1600 | sid, gfp); |
f5c1d5b2 JM |
1601 | } |
1602 | ||
1603 | /** | |
1604 | * security_context_to_sid_default - Obtain a SID for a given security context, | |
1605 | * falling back to specified default if needed. | |
1606 | * | |
1607 | * @scontext: security context | |
1608 | * @scontext_len: length in bytes | |
1609 | * @sid: security identifier, SID | |
d133a960 | 1610 | * @def_sid: default SID to assign on error |
e9fd7292 | 1611 | * @gfp_flags: the allocator get-free-page (GFP) flags |
f5c1d5b2 JM |
1612 | * |
1613 | * Obtains a SID associated with the security context that | |
1614 | * has the string representation specified by @scontext. | |
1615 | * The default SID is passed to the MLS layer to be used to allow | |
1616 | * kernel labeling of the MLS field if the MLS field is not present | |
1617 | * (for upgrading to MLS without full relabel). | |
12b29f34 | 1618 | * Implicitly forces adding of the context even if it cannot be mapped yet. |
f5c1d5b2 JM |
1619 | * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient |
1620 | * memory is available, or 0 on success. | |
1621 | */ | |
e67b7985 | 1622 | int security_context_to_sid_default(const char *scontext, u32 scontext_len, |
7bf570dc | 1623 | u32 *sid, u32 def_sid, gfp_t gfp_flags) |
f5c1d5b2 | 1624 | { |
e67b7985 | 1625 | return security_context_to_sid_core(scontext, scontext_len, |
12b29f34 SS |
1626 | sid, def_sid, gfp_flags, 1); |
1627 | } | |
1628 | ||
e67b7985 | 1629 | int security_context_to_sid_force(const char *scontext, u32 scontext_len, |
12b29f34 SS |
1630 | u32 *sid) |
1631 | { | |
e67b7985 | 1632 | return security_context_to_sid_core(scontext, scontext_len, |
12b29f34 | 1633 | sid, SECSID_NULL, GFP_KERNEL, 1); |
f5c1d5b2 JM |
1634 | } |
1635 | ||
1da177e4 | 1636 | static int compute_sid_handle_invalid_context( |
1b8b31a2 | 1637 | struct selinux_policy *policy, |
d97bd23c OM |
1638 | struct sidtab_entry *sentry, |
1639 | struct sidtab_entry *tentry, | |
1da177e4 LT |
1640 | u16 tclass, |
1641 | struct context *newcontext) | |
1642 | { | |
1b8b31a2 SS |
1643 | struct policydb *policydb = &policy->policydb; |
1644 | struct sidtab *sidtab = policy->sidtab; | |
1da177e4 LT |
1645 | char *s = NULL, *t = NULL, *n = NULL; |
1646 | u32 slen, tlen, nlen; | |
ea74a685 | 1647 | struct audit_buffer *ab; |
1da177e4 | 1648 | |
d97bd23c | 1649 | if (sidtab_entry_to_string(policydb, sidtab, sentry, &s, &slen)) |
1da177e4 | 1650 | goto out; |
d97bd23c | 1651 | if (sidtab_entry_to_string(policydb, sidtab, tentry, &t, &tlen)) |
1da177e4 | 1652 | goto out; |
aa8e712c | 1653 | if (context_struct_to_string(policydb, newcontext, &n, &nlen)) |
1da177e4 | 1654 | goto out; |
ea74a685 | 1655 | ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR); |
893c47d1 AK |
1656 | if (!ab) |
1657 | goto out; | |
ea74a685 RGB |
1658 | audit_log_format(ab, |
1659 | "op=security_compute_sid invalid_context="); | |
1660 | /* no need to record the NUL with untrusted strings */ | |
1661 | audit_log_n_untrustedstring(ab, n, nlen - 1); | |
1662 | audit_log_format(ab, " scontext=%s tcontext=%s tclass=%s", | |
1663 | s, t, sym_name(policydb, SYM_CLASSES, tclass-1)); | |
1664 | audit_log_end(ab); | |
1da177e4 LT |
1665 | out: |
1666 | kfree(s); | |
1667 | kfree(t); | |
1668 | kfree(n); | |
e67b7985 | 1669 | if (!enforcing_enabled()) |
1da177e4 LT |
1670 | return 0; |
1671 | return -EACCES; | |
1672 | } | |
1673 | ||
aa8e712c SS |
1674 | static void filename_compute_type(struct policydb *policydb, |
1675 | struct context *newcontext, | |
2667991f | 1676 | u32 stype, u32 ttype, u16 tclass, |
f50a3ec9 | 1677 | const char *objname) |
652bb9b0 | 1678 | { |
c3a27611 OM |
1679 | struct filename_trans_key ft; |
1680 | struct filename_trans_datum *datum; | |
03a4c018 EP |
1681 | |
1682 | /* | |
1683 | * Most filename trans rules are going to live in specific directories | |
1684 | * like /dev or /var/run. This bitmap will quickly skip rule searches | |
1685 | * if the ttype does not contain any rules. | |
1686 | */ | |
aa8e712c | 1687 | if (!ebitmap_get_bit(&policydb->filename_trans_ttypes, ttype)) |
03a4c018 EP |
1688 | return; |
1689 | ||
2463c26d EP |
1690 | ft.ttype = ttype; |
1691 | ft.tclass = tclass; | |
1692 | ft.name = objname; | |
1693 | ||
24def7bb | 1694 | datum = policydb_filenametr_search(policydb, &ft); |
c3a27611 OM |
1695 | while (datum) { |
1696 | if (ebitmap_get_bit(&datum->stypes, stype - 1)) { | |
1697 | newcontext->type = datum->otype; | |
1698 | return; | |
1699 | } | |
1700 | datum = datum->next; | |
1701 | } | |
652bb9b0 EP |
1702 | } |
1703 | ||
e67b7985 | 1704 | static int security_compute_sid(u32 ssid, |
1da177e4 | 1705 | u32 tsid, |
c6d3aaa4 | 1706 | u16 orig_tclass, |
7128578c | 1707 | u16 specified, |
f50a3ec9 | 1708 | const char *objname, |
c6d3aaa4 SS |
1709 | u32 *out_sid, |
1710 | bool kern) | |
1da177e4 | 1711 | { |
1b8b31a2 | 1712 | struct selinux_policy *policy; |
aa8e712c SS |
1713 | struct policydb *policydb; |
1714 | struct sidtab *sidtab; | |
9ad6e9cb | 1715 | struct class_datum *cladatum; |
d97bd23c OM |
1716 | struct context *scontext, *tcontext, newcontext; |
1717 | struct sidtab_entry *sentry, *tentry; | |
1da177e4 | 1718 | struct avtab_key avkey; |
08a12b39 | 1719 | struct avtab_node *avnode, *node; |
c6d3aaa4 | 1720 | u16 tclass; |
1da177e4 | 1721 | int rc = 0; |
6f5317e7 | 1722 | bool sock; |
1da177e4 | 1723 | |
e67b7985 | 1724 | if (!selinux_initialized()) { |
c6d3aaa4 SS |
1725 | switch (orig_tclass) { |
1726 | case SECCLASS_PROCESS: /* kernel value */ | |
1da177e4 LT |
1727 | *out_sid = ssid; |
1728 | break; | |
1729 | default: | |
1730 | *out_sid = tsid; | |
1731 | break; | |
1732 | } | |
1733 | goto out; | |
1734 | } | |
1735 | ||
9ad6e9cb OM |
1736 | retry: |
1737 | cladatum = NULL; | |
851f8a69 VY |
1738 | context_init(&newcontext); |
1739 | ||
1b8b31a2 SS |
1740 | rcu_read_lock(); |
1741 | ||
e67b7985 | 1742 | policy = rcu_dereference(selinux_state.policy); |
1da177e4 | 1743 | |
6f5317e7 | 1744 | if (kern) { |
1b8b31a2 | 1745 | tclass = unmap_class(&policy->map, orig_tclass); |
6f5317e7 HC |
1746 | sock = security_is_socket_class(orig_tclass); |
1747 | } else { | |
c6d3aaa4 | 1748 | tclass = orig_tclass; |
1b8b31a2 | 1749 | sock = security_is_socket_class(map_class(&policy->map, |
aa8e712c | 1750 | tclass)); |
6f5317e7 | 1751 | } |
c6d3aaa4 | 1752 | |
1b8b31a2 SS |
1753 | policydb = &policy->policydb; |
1754 | sidtab = policy->sidtab; | |
aa8e712c | 1755 | |
d97bd23c OM |
1756 | sentry = sidtab_search_entry(sidtab, ssid); |
1757 | if (!sentry) { | |
b54c85c1 | 1758 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 1759 | __func__, ssid); |
1da177e4 LT |
1760 | rc = -EINVAL; |
1761 | goto out_unlock; | |
1762 | } | |
d97bd23c OM |
1763 | tentry = sidtab_search_entry(sidtab, tsid); |
1764 | if (!tentry) { | |
b54c85c1 | 1765 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 1766 | __func__, tsid); |
1da177e4 LT |
1767 | rc = -EINVAL; |
1768 | goto out_unlock; | |
1769 | } | |
1770 | ||
d97bd23c OM |
1771 | scontext = &sentry->context; |
1772 | tcontext = &tentry->context; | |
1773 | ||
aa8e712c SS |
1774 | if (tclass && tclass <= policydb->p_classes.nprim) |
1775 | cladatum = policydb->class_val_to_struct[tclass - 1]; | |
aa893269 | 1776 | |
1da177e4 LT |
1777 | /* Set the user identity. */ |
1778 | switch (specified) { | |
1779 | case AVTAB_TRANSITION: | |
1780 | case AVTAB_CHANGE: | |
aa893269 EP |
1781 | if (cladatum && cladatum->default_user == DEFAULT_TARGET) { |
1782 | newcontext.user = tcontext->user; | |
1783 | } else { | |
1784 | /* notice this gets both DEFAULT_SOURCE and unset */ | |
1785 | /* Use the process user identity. */ | |
1786 | newcontext.user = scontext->user; | |
1787 | } | |
1da177e4 LT |
1788 | break; |
1789 | case AVTAB_MEMBER: | |
1790 | /* Use the related object owner. */ | |
1791 | newcontext.user = tcontext->user; | |
1792 | break; | |
1793 | } | |
1794 | ||
aa893269 EP |
1795 | /* Set the role to default values. */ |
1796 | if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { | |
1da177e4 | 1797 | newcontext.role = scontext->role; |
aa893269 EP |
1798 | } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { |
1799 | newcontext.role = tcontext->role; | |
1800 | } else { | |
4b850396 | 1801 | if ((tclass == policydb->process_class) || sock) |
aa893269 EP |
1802 | newcontext.role = scontext->role; |
1803 | else | |
1804 | newcontext.role = OBJECT_R_VAL; | |
1805 | } | |
1806 | ||
1807 | /* Set the type to default values. */ | |
eed7795d | 1808 | if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { |
1da177e4 | 1809 | newcontext.type = scontext->type; |
eed7795d | 1810 | } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { |
1da177e4 | 1811 | newcontext.type = tcontext->type; |
eed7795d | 1812 | } else { |
4b850396 | 1813 | if ((tclass == policydb->process_class) || sock) { |
eed7795d EP |
1814 | /* Use the type of process. */ |
1815 | newcontext.type = scontext->type; | |
1816 | } else { | |
1817 | /* Use the type of the related object. */ | |
1818 | newcontext.type = tcontext->type; | |
1819 | } | |
1da177e4 LT |
1820 | } |
1821 | ||
1822 | /* Look for a type transition/member/change rule. */ | |
1823 | avkey.source_type = scontext->type; | |
1824 | avkey.target_type = tcontext->type; | |
1825 | avkey.target_class = tclass; | |
782ebb99 | 1826 | avkey.specified = specified; |
08a12b39 | 1827 | avnode = avtab_search_node(&policydb->te_avtab, &avkey); |
1da177e4 LT |
1828 | |
1829 | /* If no permanent rule, also check for enabled conditional rules */ | |
08a12b39 | 1830 | if (!avnode) { |
aa8e712c | 1831 | node = avtab_search_node(&policydb->te_cond_avtab, &avkey); |
dbc74c65 | 1832 | for (; node; node = avtab_search_node_next(node, specified)) { |
782ebb99 | 1833 | if (node->key.specified & AVTAB_ENABLED) { |
08a12b39 | 1834 | avnode = node; |
1da177e4 LT |
1835 | break; |
1836 | } | |
1837 | } | |
1838 | } | |
1839 | ||
08a12b39 | 1840 | if (avnode) { |
1da177e4 | 1841 | /* Use the type from the type transition/member/change rule. */ |
08a12b39 | 1842 | newcontext.type = avnode->datum.u.data; |
1da177e4 LT |
1843 | } |
1844 | ||
4742600c | 1845 | /* if we have a objname this is a file trans check so check those rules */ |
f50a3ec9 | 1846 | if (objname) |
aa8e712c | 1847 | filename_compute_type(policydb, &newcontext, scontext->type, |
f50a3ec9 | 1848 | tcontext->type, tclass, objname); |
652bb9b0 | 1849 | |
1da177e4 | 1850 | /* Check for class-specific changes. */ |
63a312ca HC |
1851 | if (specified & AVTAB_TRANSITION) { |
1852 | /* Look for a role transition rule. */ | |
e67b2ec9 OM |
1853 | struct role_trans_datum *rtd; |
1854 | struct role_trans_key rtk = { | |
1855 | .role = scontext->role, | |
1856 | .type = tcontext->type, | |
1857 | .tclass = tclass, | |
1858 | }; | |
1859 | ||
24def7bb | 1860 | rtd = policydb_roletr_search(policydb, &rtk); |
e67b2ec9 OM |
1861 | if (rtd) |
1862 | newcontext.role = rtd->new_role; | |
1da177e4 LT |
1863 | } |
1864 | ||
1865 | /* Set the MLS attributes. | |
1866 | This is done last because it may allocate memory. */ | |
aa8e712c | 1867 | rc = mls_compute_sid(policydb, scontext, tcontext, tclass, specified, |
6f5317e7 | 1868 | &newcontext, sock); |
1da177e4 LT |
1869 | if (rc) |
1870 | goto out_unlock; | |
1871 | ||
1872 | /* Check the validity of the context. */ | |
aa8e712c | 1873 | if (!policydb_context_isvalid(policydb, &newcontext)) { |
e67b7985 | 1874 | rc = compute_sid_handle_invalid_context(policy, sentry, |
1b8b31a2 SS |
1875 | tentry, tclass, |
1876 | &newcontext); | |
1da177e4 LT |
1877 | if (rc) |
1878 | goto out_unlock; | |
1879 | } | |
1880 | /* Obtain the sid for the context. */ | |
225621c9 | 1881 | rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid); |
9ad6e9cb OM |
1882 | if (rc == -ESTALE) { |
1883 | rcu_read_unlock(); | |
1884 | context_destroy(&newcontext); | |
1885 | goto retry; | |
1886 | } | |
1da177e4 | 1887 | out_unlock: |
1b8b31a2 | 1888 | rcu_read_unlock(); |
1da177e4 LT |
1889 | context_destroy(&newcontext); |
1890 | out: | |
1891 | return rc; | |
1892 | } | |
1893 | ||
1894 | /** | |
1895 | * security_transition_sid - Compute the SID for a new subject/object. | |
1896 | * @ssid: source security identifier | |
1897 | * @tsid: target security identifier | |
1898 | * @tclass: target security class | |
e9fd7292 | 1899 | * @qstr: object name |
1da177e4 LT |
1900 | * @out_sid: security identifier for new subject/object |
1901 | * | |
1902 | * Compute a SID to use for labeling a new subject or object in the | |
1903 | * class @tclass based on a SID pair (@ssid, @tsid). | |
1904 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | |
1905 | * if insufficient memory is available, or %0 if the new SID was | |
1906 | * computed successfully. | |
1907 | */ | |
e67b7985 | 1908 | int security_transition_sid(u32 ssid, u32 tsid, u16 tclass, |
652bb9b0 | 1909 | const struct qstr *qstr, u32 *out_sid) |
1da177e4 | 1910 | { |
e67b7985 | 1911 | return security_compute_sid(ssid, tsid, tclass, |
aa8e712c | 1912 | AVTAB_TRANSITION, |
f50a3ec9 | 1913 | qstr ? qstr->name : NULL, out_sid, true); |
c6d3aaa4 SS |
1914 | } |
1915 | ||
e67b7985 | 1916 | int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, |
f50a3ec9 | 1917 | const char *objname, u32 *out_sid) |
c6d3aaa4 | 1918 | { |
e67b7985 | 1919 | return security_compute_sid(ssid, tsid, tclass, |
aa8e712c | 1920 | AVTAB_TRANSITION, |
f50a3ec9 | 1921 | objname, out_sid, false); |
1da177e4 LT |
1922 | } |
1923 | ||
1924 | /** | |
1925 | * security_member_sid - Compute the SID for member selection. | |
1926 | * @ssid: source security identifier | |
1927 | * @tsid: target security identifier | |
1928 | * @tclass: target security class | |
1929 | * @out_sid: security identifier for selected member | |
1930 | * | |
1931 | * Compute a SID to use when selecting a member of a polyinstantiated | |
1932 | * object of class @tclass based on a SID pair (@ssid, @tsid). | |
1933 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | |
1934 | * if insufficient memory is available, or %0 if the SID was | |
1935 | * computed successfully. | |
1936 | */ | |
e67b7985 | 1937 | int security_member_sid(u32 ssid, |
1da177e4 LT |
1938 | u32 tsid, |
1939 | u16 tclass, | |
1940 | u32 *out_sid) | |
1941 | { | |
e67b7985 | 1942 | return security_compute_sid(ssid, tsid, tclass, |
aa8e712c | 1943 | AVTAB_MEMBER, NULL, |
652bb9b0 | 1944 | out_sid, false); |
1da177e4 LT |
1945 | } |
1946 | ||
1947 | /** | |
1948 | * security_change_sid - Compute the SID for object relabeling. | |
1949 | * @ssid: source security identifier | |
1950 | * @tsid: target security identifier | |
1951 | * @tclass: target security class | |
1952 | * @out_sid: security identifier for selected member | |
1953 | * | |
1954 | * Compute a SID to use for relabeling an object of class @tclass | |
1955 | * based on a SID pair (@ssid, @tsid). | |
1956 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | |
1957 | * if insufficient memory is available, or %0 if the SID was | |
1958 | * computed successfully. | |
1959 | */ | |
e67b7985 | 1960 | int security_change_sid(u32 ssid, |
1da177e4 LT |
1961 | u32 tsid, |
1962 | u16 tclass, | |
1963 | u32 *out_sid) | |
1964 | { | |
e67b7985 | 1965 | return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL, |
652bb9b0 | 1966 | out_sid, false); |
b94c7e67 CS |
1967 | } |
1968 | ||
aa8e712c | 1969 | static inline int convert_context_handle_invalid_context( |
1b8b31a2 | 1970 | struct policydb *policydb, |
aa8e712c | 1971 | struct context *context) |
1da177e4 | 1972 | { |
4b02b524 EP |
1973 | char *s; |
1974 | u32 len; | |
1da177e4 | 1975 | |
e67b7985 | 1976 | if (enforcing_enabled()) |
4b02b524 EP |
1977 | return -EINVAL; |
1978 | ||
aa8e712c | 1979 | if (!context_struct_to_string(policydb, context, &s, &len)) { |
b54c85c1 | 1980 | pr_warn("SELinux: Context %s would be invalid if enforcing\n", |
1981 | s); | |
4b02b524 | 1982 | kfree(s); |
1da177e4 | 1983 | } |
4b02b524 | 1984 | return 0; |
1da177e4 LT |
1985 | } |
1986 | ||
048be156 PM |
1987 | /** |
1988 | * services_convert_context - Convert a security context across policies. | |
1989 | * @args: populated convert_context_args struct | |
1990 | * @oldc: original context | |
1991 | * @newc: converted context | |
57888f7b | 1992 | * @gfp_flags: allocation flags |
048be156 PM |
1993 | * |
1994 | * Convert the values in the security context structure @oldc from the values | |
1995 | * specified in the policy @args->oldp to the values specified in the policy | |
1996 | * @args->newp, storing the new context in @newc, and verifying that the | |
1997 | * context is valid under the new policy. | |
1da177e4 | 1998 | */ |
048be156 | 1999 | int services_convert_context(struct convert_context_args *args, |
57888f7b LT |
2000 | struct context *oldc, struct context *newc, |
2001 | gfp_t gfp_flags) | |
1da177e4 | 2002 | { |
0719aaf5 | 2003 | struct ocontext *oc; |
1da177e4 LT |
2004 | struct role_datum *role; |
2005 | struct type_datum *typdatum; | |
2006 | struct user_datum *usrdatum; | |
2007 | char *s; | |
2008 | u32 len; | |
24ed7fda | 2009 | int rc; |
1da177e4 | 2010 | |
ee1a84fd | 2011 | if (oldc->str) { |
abe3c631 | 2012 | s = kstrdup(oldc->str, gfp_flags); |
4b02b524 | 2013 | if (!s) |
ee1a84fd | 2014 | return -ENOMEM; |
4b02b524 | 2015 | |
048be156 | 2016 | rc = string_to_context_struct(args->newp, NULL, s, newc, SECSID_NULL); |
ee1a84fd | 2017 | if (rc == -EINVAL) { |
2a524393 OM |
2018 | /* |
2019 | * Retain string representation for later mapping. | |
2020 | * | |
2021 | * IMPORTANT: We need to copy the contents of oldc->str | |
2022 | * back into s again because string_to_context_struct() | |
2023 | * may have garbled it. | |
2024 | */ | |
2025 | memcpy(s, oldc->str, oldc->len); | |
ee1a84fd OM |
2026 | context_init(newc); |
2027 | newc->str = s; | |
2028 | newc->len = oldc->len; | |
2029 | return 0; | |
2030 | } | |
2031 | kfree(s); | |
2032 | if (rc) { | |
12b29f34 | 2033 | /* Other error condition, e.g. ENOMEM. */ |
b54c85c1 | 2034 | pr_err("SELinux: Unable to map context %s, rc = %d.\n", |
ee1a84fd OM |
2035 | oldc->str, -rc); |
2036 | return rc; | |
12b29f34 | 2037 | } |
ee1a84fd OM |
2038 | pr_info("SELinux: Context %s became valid (mapped).\n", |
2039 | oldc->str); | |
2040 | return 0; | |
12b29f34 SS |
2041 | } |
2042 | ||
ee1a84fd | 2043 | context_init(newc); |
1da177e4 | 2044 | |
1da177e4 | 2045 | /* Convert the user. */ |
237389e3 | 2046 | usrdatum = symtab_search(&args->newp->p_users, |
048be156 | 2047 | sym_name(args->oldp, SYM_USERS, oldc->user - 1)); |
5d55a345 | 2048 | if (!usrdatum) |
1da177e4 | 2049 | goto bad; |
ee1a84fd | 2050 | newc->user = usrdatum->value; |
1da177e4 LT |
2051 | |
2052 | /* Convert the role. */ | |
237389e3 OM |
2053 | role = symtab_search(&args->newp->p_roles, |
2054 | sym_name(args->oldp, SYM_ROLES, oldc->role - 1)); | |
5d55a345 | 2055 | if (!role) |
1da177e4 | 2056 | goto bad; |
ee1a84fd | 2057 | newc->role = role->value; |
1da177e4 LT |
2058 | |
2059 | /* Convert the type. */ | |
237389e3 | 2060 | typdatum = symtab_search(&args->newp->p_types, |
048be156 | 2061 | sym_name(args->oldp, SYM_TYPES, oldc->type - 1)); |
5d55a345 | 2062 | if (!typdatum) |
1da177e4 | 2063 | goto bad; |
ee1a84fd | 2064 | newc->type = typdatum->value; |
1da177e4 | 2065 | |
0719aaf5 GT |
2066 | /* Convert the MLS fields if dealing with MLS policies */ |
2067 | if (args->oldp->mls_enabled && args->newp->mls_enabled) { | |
ee1a84fd | 2068 | rc = mls_convert_context(args->oldp, args->newp, oldc, newc); |
0719aaf5 GT |
2069 | if (rc) |
2070 | goto bad; | |
0719aaf5 GT |
2071 | } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) { |
2072 | /* | |
2073 | * Switching between non-MLS and MLS policy: | |
2074 | * ensure that the MLS fields of the context for all | |
2075 | * existing entries in the sidtab are filled in with a | |
2076 | * suitable default value, likely taken from one of the | |
2077 | * initial SIDs. | |
2078 | */ | |
2079 | oc = args->newp->ocontexts[OCON_ISID]; | |
2080 | while (oc && oc->sid[0] != SECINITSID_UNLABELED) | |
2081 | oc = oc->next; | |
2082 | if (!oc) { | |
b54c85c1 | 2083 | pr_err("SELinux: unable to look up" |
0719aaf5 GT |
2084 | " the initial SIDs list\n"); |
2085 | goto bad; | |
2086 | } | |
ee1a84fd | 2087 | rc = mls_range_set(newc, &oc->context[0].range); |
0719aaf5 GT |
2088 | if (rc) |
2089 | goto bad; | |
2090 | } | |
1da177e4 LT |
2091 | |
2092 | /* Check the validity of the new context. */ | |
ee1a84fd | 2093 | if (!policydb_context_isvalid(args->newp, newc)) { |
e67b7985 | 2094 | rc = convert_context_handle_invalid_context(args->oldp, oldc); |
1da177e4 LT |
2095 | if (rc) |
2096 | goto bad; | |
2097 | } | |
2098 | ||
ee1a84fd | 2099 | return 0; |
1da177e4 | 2100 | bad: |
12b29f34 | 2101 | /* Map old representation to string and save it. */ |
ee1a84fd | 2102 | rc = context_struct_to_string(args->oldp, oldc, &s, &len); |
4b02b524 EP |
2103 | if (rc) |
2104 | return rc; | |
ee1a84fd OM |
2105 | context_destroy(newc); |
2106 | newc->str = s; | |
2107 | newc->len = len; | |
b54c85c1 | 2108 | pr_info("SELinux: Context %s became invalid (unmapped).\n", |
ee1a84fd OM |
2109 | newc->str); |
2110 | return 0; | |
1da177e4 LT |
2111 | } |
2112 | ||
e67b7985 | 2113 | static void security_load_policycaps(struct selinux_policy *policy) |
3bb56b25 | 2114 | { |
46169802 | 2115 | struct policydb *p; |
4dc2fce3 SS |
2116 | unsigned int i; |
2117 | struct ebitmap_node *node; | |
2118 | ||
1b8b31a2 | 2119 | p = &policy->policydb; |
46169802 | 2120 | |
e67b7985 SS |
2121 | for (i = 0; i < ARRAY_SIZE(selinux_state.policycap); i++) |
2122 | WRITE_ONCE(selinux_state.policycap[i], | |
e8ba53d0 | 2123 | ebitmap_get_bit(&p->policycaps, i)); |
4dc2fce3 SS |
2124 | |
2125 | for (i = 0; i < ARRAY_SIZE(selinux_policycap_names); i++) | |
2126 | pr_info("SELinux: policy capability %s=%d\n", | |
2127 | selinux_policycap_names[i], | |
aa8e712c | 2128 | ebitmap_get_bit(&p->policycaps, i)); |
4dc2fce3 | 2129 | |
aa8e712c | 2130 | ebitmap_for_each_positive_bit(&p->policycaps, node, i) { |
4dc2fce3 SS |
2131 | if (i >= ARRAY_SIZE(selinux_policycap_names)) |
2132 | pr_info("SELinux: unknown policy capability %u\n", | |
2133 | i); | |
2134 | } | |
3bb56b25 PM |
2135 | } |
2136 | ||
1b8b31a2 SS |
2137 | static int security_preserve_bools(struct selinux_policy *oldpolicy, |
2138 | struct selinux_policy *newpolicy); | |
1da177e4 | 2139 | |
46169802 SS |
2140 | static void selinux_policy_free(struct selinux_policy *policy) |
2141 | { | |
2142 | if (!policy) | |
2143 | return; | |
2144 | ||
c7c556f1 | 2145 | sidtab_destroy(policy->sidtab); |
46169802 | 2146 | kfree(policy->map.mapping); |
0256b0aa DC |
2147 | policydb_destroy(&policy->policydb); |
2148 | kfree(policy->sidtab); | |
46169802 SS |
2149 | kfree(policy); |
2150 | } | |
2151 | ||
1b8b31a2 SS |
2152 | static void selinux_policy_cond_free(struct selinux_policy *policy) |
2153 | { | |
2154 | cond_policydb_destroy_dup(&policy->policydb); | |
2155 | kfree(policy); | |
2156 | } | |
2157 | ||
e67b7985 | 2158 | void selinux_policy_cancel(struct selinux_load_state *load_state) |
02a52c5c | 2159 | { |
e67b7985 | 2160 | struct selinux_state *state = &selinux_state; |
1b8b31a2 SS |
2161 | struct selinux_policy *oldpolicy; |
2162 | ||
9ff9abc4 SS |
2163 | oldpolicy = rcu_dereference_protected(state->policy, |
2164 | lockdep_is_held(&state->policy_mutex)); | |
1b8b31a2 SS |
2165 | |
2166 | sidtab_cancel_convert(oldpolicy->sidtab); | |
6406887a OM |
2167 | selinux_policy_free(load_state->policy); |
2168 | kfree(load_state->convert_data); | |
02a52c5c SS |
2169 | } |
2170 | ||
e67b7985 | 2171 | static void selinux_notify_policy_change(u32 seqno) |
c7c556f1 SS |
2172 | { |
2173 | /* Flush external caches and notify userspace of policy load */ | |
e67b7985 | 2174 | avc_ss_reset(seqno); |
c7c556f1 | 2175 | selnl_notify_policyload(seqno); |
e67b7985 | 2176 | selinux_status_update_policyload(seqno); |
c7c556f1 SS |
2177 | selinux_netlbl_cache_invalidate(); |
2178 | selinux_xfrm_notify_policyload(); | |
e67b7985 | 2179 | selinux_ima_measure_state_locked(); |
c7c556f1 SS |
2180 | } |
2181 | ||
e67b7985 | 2182 | void selinux_policy_commit(struct selinux_load_state *load_state) |
46169802 | 2183 | { |
e67b7985 | 2184 | struct selinux_state *state = &selinux_state; |
6406887a | 2185 | struct selinux_policy *oldpolicy, *newpolicy = load_state->policy; |
9ad6e9cb | 2186 | unsigned long flags; |
46169802 SS |
2187 | u32 seqno; |
2188 | ||
9ff9abc4 SS |
2189 | oldpolicy = rcu_dereference_protected(state->policy, |
2190 | lockdep_is_held(&state->policy_mutex)); | |
46169802 SS |
2191 | |
2192 | /* If switching between different policy types, log MLS status */ | |
46169802 SS |
2193 | if (oldpolicy) { |
2194 | if (oldpolicy->policydb.mls_enabled && !newpolicy->policydb.mls_enabled) | |
2195 | pr_info("SELinux: Disabling MLS support...\n"); | |
2196 | else if (!oldpolicy->policydb.mls_enabled && newpolicy->policydb.mls_enabled) | |
2197 | pr_info("SELinux: Enabling MLS support...\n"); | |
2198 | } | |
2199 | ||
1b8b31a2 SS |
2200 | /* Set latest granting seqno for new policy. */ |
2201 | if (oldpolicy) | |
2202 | newpolicy->latest_granting = oldpolicy->latest_granting + 1; | |
2203 | else | |
2204 | newpolicy->latest_granting = 1; | |
2205 | seqno = newpolicy->latest_granting; | |
2206 | ||
46169802 | 2207 | /* Install the new policy. */ |
9ad6e9cb OM |
2208 | if (oldpolicy) { |
2209 | sidtab_freeze_begin(oldpolicy->sidtab, &flags); | |
2210 | rcu_assign_pointer(state->policy, newpolicy); | |
2211 | sidtab_freeze_end(oldpolicy->sidtab, &flags); | |
2212 | } else { | |
2213 | rcu_assign_pointer(state->policy, newpolicy); | |
2214 | } | |
46169802 SS |
2215 | |
2216 | /* Load the policycaps from the new policy */ | |
e67b7985 | 2217 | security_load_policycaps(newpolicy); |
46169802 | 2218 | |
e67b7985 | 2219 | if (!selinux_initialized()) { |
46169802 SS |
2220 | /* |
2221 | * After first policy load, the security server is | |
2222 | * marked as initialized and ready to handle requests and | |
2223 | * any objects created prior to policy load are then labeled. | |
2224 | */ | |
e67b7985 | 2225 | selinux_mark_initialized(); |
46169802 SS |
2226 | selinux_complete_init(); |
2227 | } | |
2228 | ||
2229 | /* Free the old policy */ | |
1b8b31a2 | 2230 | synchronize_rcu(); |
46169802 | 2231 | selinux_policy_free(oldpolicy); |
6406887a | 2232 | kfree(load_state->convert_data); |
46169802 | 2233 | |
c7c556f1 | 2234 | /* Notify others of the policy change */ |
e67b7985 | 2235 | selinux_notify_policy_change(seqno); |
46169802 SS |
2236 | } |
2237 | ||
1da177e4 LT |
2238 | /** |
2239 | * security_load_policy - Load a security policy configuration. | |
2240 | * @data: binary policy data | |
2241 | * @len: length of data in bytes | |
e9fd7292 | 2242 | * @load_state: policy load state |
1da177e4 LT |
2243 | * |
2244 | * Load a new set of security policy configuration data, | |
2245 | * validate it and convert the SID table as necessary. | |
2246 | * This function will flush the access vector cache after | |
2247 | * loading the new policy. | |
2248 | */ | |
e67b7985 | 2249 | int security_load_policy(void *data, size_t len, |
6406887a | 2250 | struct selinux_load_state *load_state) |
1da177e4 | 2251 | { |
e67b7985 | 2252 | struct selinux_state *state = &selinux_state; |
1b8b31a2 | 2253 | struct selinux_policy *newpolicy, *oldpolicy; |
6406887a | 2254 | struct selinux_policy_convert_data *convert_data; |
1da177e4 LT |
2255 | int rc = 0; |
2256 | struct policy_file file = { data, len }, *fp = &file; | |
2257 | ||
46169802 SS |
2258 | newpolicy = kzalloc(sizeof(*newpolicy), GFP_KERNEL); |
2259 | if (!newpolicy) | |
dd89b9d9 | 2260 | return -ENOMEM; |
aa8e712c | 2261 | |
c7c556f1 | 2262 | newpolicy->sidtab = kzalloc(sizeof(*newpolicy->sidtab), GFP_KERNEL); |
0256b0aa DC |
2263 | if (!newpolicy->sidtab) { |
2264 | rc = -ENOMEM; | |
2265 | goto err_policy; | |
2266 | } | |
c7c556f1 | 2267 | |
46169802 SS |
2268 | rc = policydb_read(&newpolicy->policydb, fp); |
2269 | if (rc) | |
0256b0aa | 2270 | goto err_sidtab; |
1da177e4 | 2271 | |
46169802 SS |
2272 | newpolicy->policydb.len = len; |
2273 | rc = selinux_set_mapping(&newpolicy->policydb, secclass_map, | |
2274 | &newpolicy->map); | |
2275 | if (rc) | |
0256b0aa | 2276 | goto err_policydb; |
dd89b9d9 | 2277 | |
c7c556f1 | 2278 | rc = policydb_load_isids(&newpolicy->policydb, newpolicy->sidtab); |
24ed7fda | 2279 | if (rc) { |
46169802 | 2280 | pr_err("SELinux: unable to load the initial SIDs\n"); |
0256b0aa | 2281 | goto err_mapping; |
24ed7fda | 2282 | } |
1da177e4 | 2283 | |
e67b7985 | 2284 | if (!selinux_initialized()) { |
46169802 | 2285 | /* First policy load, so no need to preserve state from old policy */ |
6406887a OM |
2286 | load_state->policy = newpolicy; |
2287 | load_state->convert_data = NULL; | |
46169802 | 2288 | return 0; |
12b29f34 | 2289 | } |
1da177e4 | 2290 | |
9ff9abc4 SS |
2291 | oldpolicy = rcu_dereference_protected(state->policy, |
2292 | lockdep_is_held(&state->policy_mutex)); | |
1b8b31a2 | 2293 | |
46169802 | 2294 | /* Preserve active boolean values from the old policy */ |
1b8b31a2 | 2295 | rc = security_preserve_bools(oldpolicy, newpolicy); |
e900a7d9 | 2296 | if (rc) { |
b54c85c1 | 2297 | pr_err("SELinux: unable to preserve booleans\n"); |
0256b0aa | 2298 | goto err_free_isids; |
e900a7d9 SS |
2299 | } |
2300 | ||
048be156 PM |
2301 | /* |
2302 | * Convert the internal representations of contexts | |
2303 | * in the new SID table. | |
2304 | */ | |
2305 | ||
6406887a OM |
2306 | convert_data = kmalloc(sizeof(*convert_data), GFP_KERNEL); |
2307 | if (!convert_data) { | |
2308 | rc = -ENOMEM; | |
2309 | goto err_free_isids; | |
2310 | } | |
2311 | ||
6406887a OM |
2312 | convert_data->args.oldp = &oldpolicy->policydb; |
2313 | convert_data->args.newp = &newpolicy->policydb; | |
ee1a84fd | 2314 | |
6406887a OM |
2315 | convert_data->sidtab_params.args = &convert_data->args; |
2316 | convert_data->sidtab_params.target = newpolicy->sidtab; | |
ee1a84fd | 2317 | |
6406887a | 2318 | rc = sidtab_convert(oldpolicy->sidtab, &convert_data->sidtab_params); |
0719aaf5 | 2319 | if (rc) { |
b54c85c1 | 2320 | pr_err("SELinux: unable to convert the internal" |
0719aaf5 GT |
2321 | " representation of contexts in the new SID" |
2322 | " table\n"); | |
6406887a | 2323 | goto err_free_convert_data; |
0719aaf5 | 2324 | } |
1da177e4 | 2325 | |
6406887a OM |
2326 | load_state->policy = newpolicy; |
2327 | load_state->convert_data = convert_data; | |
46169802 | 2328 | return 0; |
0256b0aa | 2329 | |
6406887a OM |
2330 | err_free_convert_data: |
2331 | kfree(convert_data); | |
0256b0aa DC |
2332 | err_free_isids: |
2333 | sidtab_destroy(newpolicy->sidtab); | |
2334 | err_mapping: | |
2335 | kfree(newpolicy->map.mapping); | |
2336 | err_policydb: | |
2337 | policydb_destroy(&newpolicy->policydb); | |
2338 | err_sidtab: | |
2339 | kfree(newpolicy->sidtab); | |
2340 | err_policy: | |
2341 | kfree(newpolicy); | |
2342 | ||
b5495b42 | 2343 | return rc; |
1da177e4 LT |
2344 | } |
2345 | ||
cbfcd13b OM |
2346 | /** |
2347 | * ocontext_to_sid - Helper to safely get sid for an ocontext | |
2348 | * @sidtab: SID table | |
2349 | * @c: ocontext structure | |
2350 | * @index: index of the context entry (0 or 1) | |
2351 | * @out_sid: pointer to the resulting SID value | |
2352 | * | |
2353 | * For all ocontexts except OCON_ISID the SID fields are populated | |
2354 | * on-demand when needed. Since updating the SID value is an SMP-sensitive | |
2355 | * operation, this helper must be used to do that safely. | |
2356 | * | |
2357 | * WARNING: This function may return -ESTALE, indicating that the caller | |
2358 | * must retry the operation after re-acquiring the policy pointer! | |
2359 | */ | |
2360 | static int ocontext_to_sid(struct sidtab *sidtab, struct ocontext *c, | |
2361 | size_t index, u32 *out_sid) | |
2362 | { | |
2363 | int rc; | |
2364 | u32 sid; | |
2365 | ||
2366 | /* Ensure the associated sidtab entry is visible to this thread. */ | |
2367 | sid = smp_load_acquire(&c->sid[index]); | |
2368 | if (!sid) { | |
2369 | rc = sidtab_context_to_sid(sidtab, &c->context[index], &sid); | |
2370 | if (rc) | |
2371 | return rc; | |
2372 | ||
2373 | /* | |
2374 | * Ensure the new sidtab entry is visible to other threads | |
2375 | * when they see the SID. | |
2376 | */ | |
2377 | smp_store_release(&c->sid[index], sid); | |
2378 | } | |
2379 | *out_sid = sid; | |
2380 | return 0; | |
2381 | } | |
2382 | ||
1da177e4 LT |
2383 | /** |
2384 | * security_port_sid - Obtain the SID for a port. | |
1da177e4 LT |
2385 | * @protocol: protocol number |
2386 | * @port: port number | |
2387 | * @out_sid: security identifier | |
2388 | */ | |
e67b7985 | 2389 | int security_port_sid(u8 protocol, u16 port, u32 *out_sid) |
1da177e4 | 2390 | { |
1b8b31a2 | 2391 | struct selinux_policy *policy; |
aa8e712c | 2392 | struct policydb *policydb; |
225621c9 | 2393 | struct sidtab *sidtab; |
1da177e4 | 2394 | struct ocontext *c; |
9ad6e9cb | 2395 | int rc; |
1da177e4 | 2396 | |
e67b7985 | 2397 | if (!selinux_initialized()) { |
37ea433c SS |
2398 | *out_sid = SECINITSID_PORT; |
2399 | return 0; | |
2400 | } | |
2401 | ||
9ad6e9cb OM |
2402 | retry: |
2403 | rc = 0; | |
1b8b31a2 | 2404 | rcu_read_lock(); |
e67b7985 | 2405 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2406 | policydb = &policy->policydb; |
2407 | sidtab = policy->sidtab; | |
1da177e4 | 2408 | |
aa8e712c | 2409 | c = policydb->ocontexts[OCON_PORT]; |
1da177e4 LT |
2410 | while (c) { |
2411 | if (c->u.port.protocol == protocol && | |
2412 | c->u.port.low_port <= port && | |
2413 | c->u.port.high_port >= port) | |
2414 | break; | |
2415 | c = c->next; | |
2416 | } | |
2417 | ||
2418 | if (c) { | |
cbfcd13b OM |
2419 | rc = ocontext_to_sid(sidtab, c, 0, out_sid); |
2420 | if (rc == -ESTALE) { | |
2421 | rcu_read_unlock(); | |
2422 | goto retry; | |
1da177e4 | 2423 | } |
cbfcd13b OM |
2424 | if (rc) |
2425 | goto out; | |
1da177e4 LT |
2426 | } else { |
2427 | *out_sid = SECINITSID_PORT; | |
2428 | } | |
2429 | ||
2430 | out: | |
1b8b31a2 | 2431 | rcu_read_unlock(); |
1da177e4 LT |
2432 | return rc; |
2433 | } | |
2434 | ||
cfc4d882 | 2435 | /** |
d0a83314 | 2436 | * security_ib_pkey_sid - Obtain the SID for a pkey. |
cfc4d882 DJ |
2437 | * @subnet_prefix: Subnet Prefix |
2438 | * @pkey_num: pkey number | |
2439 | * @out_sid: security identifier | |
2440 | */ | |
e67b7985 | 2441 | int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid) |
cfc4d882 | 2442 | { |
1b8b31a2 | 2443 | struct selinux_policy *policy; |
aa8e712c | 2444 | struct policydb *policydb; |
225621c9 | 2445 | struct sidtab *sidtab; |
cfc4d882 | 2446 | struct ocontext *c; |
9ad6e9cb | 2447 | int rc; |
cfc4d882 | 2448 | |
e67b7985 | 2449 | if (!selinux_initialized()) { |
37ea433c SS |
2450 | *out_sid = SECINITSID_UNLABELED; |
2451 | return 0; | |
2452 | } | |
2453 | ||
9ad6e9cb OM |
2454 | retry: |
2455 | rc = 0; | |
1b8b31a2 | 2456 | rcu_read_lock(); |
e67b7985 | 2457 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2458 | policydb = &policy->policydb; |
2459 | sidtab = policy->sidtab; | |
cfc4d882 | 2460 | |
aa8e712c | 2461 | c = policydb->ocontexts[OCON_IBPKEY]; |
cfc4d882 DJ |
2462 | while (c) { |
2463 | if (c->u.ibpkey.low_pkey <= pkey_num && | |
2464 | c->u.ibpkey.high_pkey >= pkey_num && | |
2465 | c->u.ibpkey.subnet_prefix == subnet_prefix) | |
2466 | break; | |
2467 | ||
2468 | c = c->next; | |
2469 | } | |
2470 | ||
2471 | if (c) { | |
cbfcd13b OM |
2472 | rc = ocontext_to_sid(sidtab, c, 0, out_sid); |
2473 | if (rc == -ESTALE) { | |
2474 | rcu_read_unlock(); | |
2475 | goto retry; | |
cfc4d882 | 2476 | } |
cbfcd13b OM |
2477 | if (rc) |
2478 | goto out; | |
cfc4d882 DJ |
2479 | } else |
2480 | *out_sid = SECINITSID_UNLABELED; | |
2481 | ||
2482 | out: | |
1b8b31a2 | 2483 | rcu_read_unlock(); |
cfc4d882 DJ |
2484 | return rc; |
2485 | } | |
2486 | ||
ab861dfc DJ |
2487 | /** |
2488 | * security_ib_endport_sid - Obtain the SID for a subnet management interface. | |
2489 | * @dev_name: device name | |
e9fd7292 | 2490 | * @port_num: port number |
ab861dfc DJ |
2491 | * @out_sid: security identifier |
2492 | */ | |
e67b7985 | 2493 | int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid) |
ab861dfc | 2494 | { |
1b8b31a2 | 2495 | struct selinux_policy *policy; |
aa8e712c | 2496 | struct policydb *policydb; |
225621c9 | 2497 | struct sidtab *sidtab; |
ab861dfc | 2498 | struct ocontext *c; |
9ad6e9cb | 2499 | int rc; |
ab861dfc | 2500 | |
e67b7985 | 2501 | if (!selinux_initialized()) { |
37ea433c SS |
2502 | *out_sid = SECINITSID_UNLABELED; |
2503 | return 0; | |
2504 | } | |
2505 | ||
9ad6e9cb OM |
2506 | retry: |
2507 | rc = 0; | |
1b8b31a2 | 2508 | rcu_read_lock(); |
e67b7985 | 2509 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2510 | policydb = &policy->policydb; |
2511 | sidtab = policy->sidtab; | |
aa8e712c SS |
2512 | |
2513 | c = policydb->ocontexts[OCON_IBENDPORT]; | |
ab861dfc DJ |
2514 | while (c) { |
2515 | if (c->u.ibendport.port == port_num && | |
2516 | !strncmp(c->u.ibendport.dev_name, | |
2517 | dev_name, | |
2518 | IB_DEVICE_NAME_MAX)) | |
2519 | break; | |
2520 | ||
2521 | c = c->next; | |
2522 | } | |
2523 | ||
2524 | if (c) { | |
cbfcd13b OM |
2525 | rc = ocontext_to_sid(sidtab, c, 0, out_sid); |
2526 | if (rc == -ESTALE) { | |
2527 | rcu_read_unlock(); | |
2528 | goto retry; | |
ab861dfc | 2529 | } |
cbfcd13b OM |
2530 | if (rc) |
2531 | goto out; | |
ab861dfc DJ |
2532 | } else |
2533 | *out_sid = SECINITSID_UNLABELED; | |
2534 | ||
2535 | out: | |
1b8b31a2 | 2536 | rcu_read_unlock(); |
ab861dfc DJ |
2537 | return rc; |
2538 | } | |
2539 | ||
1da177e4 LT |
2540 | /** |
2541 | * security_netif_sid - Obtain the SID for a network interface. | |
2542 | * @name: interface name | |
2543 | * @if_sid: interface SID | |
1da177e4 | 2544 | */ |
e67b7985 | 2545 | int security_netif_sid(char *name, u32 *if_sid) |
1da177e4 | 2546 | { |
1b8b31a2 | 2547 | struct selinux_policy *policy; |
aa8e712c | 2548 | struct policydb *policydb; |
225621c9 | 2549 | struct sidtab *sidtab; |
9ad6e9cb | 2550 | int rc; |
1da177e4 LT |
2551 | struct ocontext *c; |
2552 | ||
e67b7985 | 2553 | if (!selinux_initialized()) { |
37ea433c SS |
2554 | *if_sid = SECINITSID_NETIF; |
2555 | return 0; | |
2556 | } | |
2557 | ||
9ad6e9cb OM |
2558 | retry: |
2559 | rc = 0; | |
1b8b31a2 | 2560 | rcu_read_lock(); |
e67b7985 | 2561 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2562 | policydb = &policy->policydb; |
2563 | sidtab = policy->sidtab; | |
1da177e4 | 2564 | |
aa8e712c | 2565 | c = policydb->ocontexts[OCON_NETIF]; |
1da177e4 LT |
2566 | while (c) { |
2567 | if (strcmp(name, c->u.name) == 0) | |
2568 | break; | |
2569 | c = c->next; | |
2570 | } | |
2571 | ||
2572 | if (c) { | |
cbfcd13b OM |
2573 | rc = ocontext_to_sid(sidtab, c, 0, if_sid); |
2574 | if (rc == -ESTALE) { | |
2575 | rcu_read_unlock(); | |
2576 | goto retry; | |
1da177e4 | 2577 | } |
cbfcd13b OM |
2578 | if (rc) |
2579 | goto out; | |
e8bfdb9d | 2580 | } else |
1da177e4 | 2581 | *if_sid = SECINITSID_NETIF; |
1da177e4 LT |
2582 | |
2583 | out: | |
1b8b31a2 | 2584 | rcu_read_unlock(); |
1da177e4 LT |
2585 | return rc; |
2586 | } | |
2587 | ||
2588 | static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) | |
2589 | { | |
2590 | int i, fail = 0; | |
2591 | ||
5d55a345 EP |
2592 | for (i = 0; i < 4; i++) |
2593 | if (addr[i] != (input[i] & mask[i])) { | |
1da177e4 LT |
2594 | fail = 1; |
2595 | break; | |
2596 | } | |
2597 | ||
2598 | return !fail; | |
2599 | } | |
2600 | ||
2601 | /** | |
2602 | * security_node_sid - Obtain the SID for a node (host). | |
2603 | * @domain: communication domain aka address family | |
2604 | * @addrp: address | |
2605 | * @addrlen: address length in bytes | |
2606 | * @out_sid: security identifier | |
2607 | */ | |
e67b7985 | 2608 | int security_node_sid(u16 domain, |
1da177e4 LT |
2609 | void *addrp, |
2610 | u32 addrlen, | |
2611 | u32 *out_sid) | |
2612 | { | |
1b8b31a2 | 2613 | struct selinux_policy *policy; |
aa8e712c | 2614 | struct policydb *policydb; |
225621c9 | 2615 | struct sidtab *sidtab; |
4b02b524 | 2616 | int rc; |
1da177e4 LT |
2617 | struct ocontext *c; |
2618 | ||
e67b7985 | 2619 | if (!selinux_initialized()) { |
37ea433c SS |
2620 | *out_sid = SECINITSID_NODE; |
2621 | return 0; | |
2622 | } | |
2623 | ||
9ad6e9cb | 2624 | retry: |
1b8b31a2 | 2625 | rcu_read_lock(); |
e67b7985 | 2626 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2627 | policydb = &policy->policydb; |
2628 | sidtab = policy->sidtab; | |
1da177e4 LT |
2629 | |
2630 | switch (domain) { | |
2631 | case AF_INET: { | |
2632 | u32 addr; | |
2633 | ||
4b02b524 EP |
2634 | rc = -EINVAL; |
2635 | if (addrlen != sizeof(u32)) | |
1da177e4 | 2636 | goto out; |
1da177e4 LT |
2637 | |
2638 | addr = *((u32 *)addrp); | |
2639 | ||
aa8e712c | 2640 | c = policydb->ocontexts[OCON_NODE]; |
1da177e4 LT |
2641 | while (c) { |
2642 | if (c->u.node.addr == (addr & c->u.node.mask)) | |
2643 | break; | |
2644 | c = c->next; | |
2645 | } | |
2646 | break; | |
2647 | } | |
2648 | ||
2649 | case AF_INET6: | |
4b02b524 EP |
2650 | rc = -EINVAL; |
2651 | if (addrlen != sizeof(u64) * 2) | |
1da177e4 | 2652 | goto out; |
aa8e712c | 2653 | c = policydb->ocontexts[OCON_NODE6]; |
1da177e4 LT |
2654 | while (c) { |
2655 | if (match_ipv6_addrmask(addrp, c->u.node6.addr, | |
2656 | c->u.node6.mask)) | |
2657 | break; | |
2658 | c = c->next; | |
2659 | } | |
2660 | break; | |
2661 | ||
2662 | default: | |
4b02b524 | 2663 | rc = 0; |
1da177e4 LT |
2664 | *out_sid = SECINITSID_NODE; |
2665 | goto out; | |
2666 | } | |
2667 | ||
2668 | if (c) { | |
cbfcd13b OM |
2669 | rc = ocontext_to_sid(sidtab, c, 0, out_sid); |
2670 | if (rc == -ESTALE) { | |
2671 | rcu_read_unlock(); | |
2672 | goto retry; | |
1da177e4 | 2673 | } |
cbfcd13b OM |
2674 | if (rc) |
2675 | goto out; | |
1da177e4 LT |
2676 | } else { |
2677 | *out_sid = SECINITSID_NODE; | |
2678 | } | |
2679 | ||
4b02b524 | 2680 | rc = 0; |
1da177e4 | 2681 | out: |
1b8b31a2 | 2682 | rcu_read_unlock(); |
1da177e4 LT |
2683 | return rc; |
2684 | } | |
2685 | ||
2686 | #define SIDS_NEL 25 | |
2687 | ||
2688 | /** | |
2689 | * security_get_user_sids - Obtain reachable SIDs for a user. | |
2690 | * @fromsid: starting SID | |
2691 | * @username: username | |
2692 | * @sids: array of reachable SIDs for user | |
2693 | * @nel: number of elements in @sids | |
2694 | * | |
2695 | * Generate the set of SIDs for legal security contexts | |
2696 | * for a given user that can be reached by @fromsid. | |
2697 | * Set *@sids to point to a dynamically allocated | |
2698 | * array containing the set of SIDs. Set *@nel to the | |
2699 | * number of elements in the array. | |
2700 | */ | |
2701 | ||
e67b7985 | 2702 | int security_get_user_sids(u32 fromsid, |
5d55a345 | 2703 | char *username, |
1da177e4 LT |
2704 | u32 **sids, |
2705 | u32 *nel) | |
2706 | { | |
1b8b31a2 | 2707 | struct selinux_policy *policy; |
aa8e712c SS |
2708 | struct policydb *policydb; |
2709 | struct sidtab *sidtab; | |
1da177e4 | 2710 | struct context *fromcon, usercon; |
2c3c05db | 2711 | u32 *mysids = NULL, *mysids2, sid; |
9ad6e9cb | 2712 | u32 i, j, mynel, maxnel = SIDS_NEL; |
1da177e4 LT |
2713 | struct user_datum *user; |
2714 | struct role_datum *role; | |
782ebb99 | 2715 | struct ebitmap_node *rnode, *tnode; |
9ad6e9cb | 2716 | int rc; |
1da177e4 | 2717 | |
2c3c05db SS |
2718 | *sids = NULL; |
2719 | *nel = 0; | |
2720 | ||
e67b7985 | 2721 | if (!selinux_initialized()) |
9ad6e9cb OM |
2722 | return 0; |
2723 | ||
2724 | mysids = kcalloc(maxnel, sizeof(*mysids), GFP_KERNEL); | |
2725 | if (!mysids) | |
2726 | return -ENOMEM; | |
1da177e4 | 2727 | |
9ad6e9cb OM |
2728 | retry: |
2729 | mynel = 0; | |
1b8b31a2 | 2730 | rcu_read_lock(); |
e67b7985 | 2731 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2732 | policydb = &policy->policydb; |
2733 | sidtab = policy->sidtab; | |
1da177e4 | 2734 | |
12b29f34 SS |
2735 | context_init(&usercon); |
2736 | ||
4b02b524 | 2737 | rc = -EINVAL; |
aa8e712c | 2738 | fromcon = sidtab_search(sidtab, fromsid); |
4b02b524 | 2739 | if (!fromcon) |
1da177e4 | 2740 | goto out_unlock; |
1da177e4 | 2741 | |
4b02b524 | 2742 | rc = -EINVAL; |
237389e3 | 2743 | user = symtab_search(&policydb->p_users, username); |
4b02b524 | 2744 | if (!user) |
1da177e4 | 2745 | goto out_unlock; |
4b02b524 | 2746 | |
1da177e4 LT |
2747 | usercon.user = user->value; |
2748 | ||
9fe79ad1 | 2749 | ebitmap_for_each_positive_bit(&user->roles, rnode, i) { |
aa8e712c | 2750 | role = policydb->role_val_to_struct[i]; |
c1a7368a | 2751 | usercon.role = i + 1; |
9fe79ad1 | 2752 | ebitmap_for_each_positive_bit(&role->types, tnode, j) { |
c1a7368a | 2753 | usercon.type = j + 1; |
1da177e4 | 2754 | |
aa8e712c SS |
2755 | if (mls_setup_user_range(policydb, fromcon, user, |
2756 | &usercon)) | |
1da177e4 LT |
2757 | continue; |
2758 | ||
225621c9 | 2759 | rc = sidtab_context_to_sid(sidtab, &usercon, &sid); |
9ad6e9cb OM |
2760 | if (rc == -ESTALE) { |
2761 | rcu_read_unlock(); | |
2762 | goto retry; | |
2763 | } | |
2c3c05db | 2764 | if (rc) |
1da177e4 | 2765 | goto out_unlock; |
1da177e4 LT |
2766 | if (mynel < maxnel) { |
2767 | mysids[mynel++] = sid; | |
2768 | } else { | |
4b02b524 | 2769 | rc = -ENOMEM; |
1da177e4 | 2770 | maxnel += SIDS_NEL; |
89d155ef | 2771 | mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC); |
4b02b524 | 2772 | if (!mysids2) |
1da177e4 | 2773 | goto out_unlock; |
1da177e4 LT |
2774 | memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); |
2775 | kfree(mysids); | |
2776 | mysids = mysids2; | |
2777 | mysids[mynel++] = sid; | |
2778 | } | |
2779 | } | |
2780 | } | |
4b02b524 | 2781 | rc = 0; |
1da177e4 | 2782 | out_unlock: |
1b8b31a2 | 2783 | rcu_read_unlock(); |
2c3c05db SS |
2784 | if (rc || !mynel) { |
2785 | kfree(mysids); | |
9ad6e9cb | 2786 | return rc; |
2c3c05db SS |
2787 | } |
2788 | ||
4b02b524 | 2789 | rc = -ENOMEM; |
2c3c05db SS |
2790 | mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL); |
2791 | if (!mysids2) { | |
2c3c05db | 2792 | kfree(mysids); |
9ad6e9cb | 2793 | return rc; |
2c3c05db SS |
2794 | } |
2795 | for (i = 0, j = 0; i < mynel; i++) { | |
f01e1af4 | 2796 | struct av_decision dummy_avd; |
e67b7985 | 2797 | rc = avc_has_perm_noaudit(fromsid, mysids[i], |
c6d3aaa4 | 2798 | SECCLASS_PROCESS, /* kernel value */ |
2c3c05db | 2799 | PROCESS__TRANSITION, AVC_STRICT, |
f01e1af4 | 2800 | &dummy_avd); |
2c3c05db SS |
2801 | if (!rc) |
2802 | mysids2[j++] = mysids[i]; | |
2803 | cond_resched(); | |
2804 | } | |
2c3c05db SS |
2805 | kfree(mysids); |
2806 | *sids = mysids2; | |
2807 | *nel = j; | |
9ad6e9cb | 2808 | return 0; |
1da177e4 LT |
2809 | } |
2810 | ||
2811 | /** | |
f31e7994 | 2812 | * __security_genfs_sid - Helper to obtain a SID for a file in a filesystem |
e9fd7292 | 2813 | * @policy: policy |
1da177e4 LT |
2814 | * @fstype: filesystem type |
2815 | * @path: path from root of mount | |
e9fd7292 | 2816 | * @orig_sclass: file security class |
1da177e4 LT |
2817 | * @sid: SID for path |
2818 | * | |
2819 | * Obtain a SID to use for a file in a filesystem that | |
2820 | * cannot support xattr or use a fixed labeling behavior like | |
2821 | * transition SIDs or task SIDs. | |
9ad6e9cb OM |
2822 | * |
2823 | * WARNING: This function may return -ESTALE, indicating that the caller | |
2824 | * must retry the operation after re-acquiring the policy pointer! | |
1da177e4 | 2825 | */ |
02a52c5c | 2826 | static inline int __security_genfs_sid(struct selinux_policy *policy, |
aa8e712c | 2827 | const char *fstype, |
08df4905 | 2828 | const char *path, |
f31e7994 WL |
2829 | u16 orig_sclass, |
2830 | u32 *sid) | |
1da177e4 | 2831 | { |
02a52c5c | 2832 | struct policydb *policydb = &policy->policydb; |
c7c556f1 | 2833 | struct sidtab *sidtab = policy->sidtab; |
c6d3aaa4 | 2834 | u16 sclass; |
1da177e4 LT |
2835 | struct genfs *genfs; |
2836 | struct ocontext *c; | |
cbfcd13b | 2837 | int cmp = 0; |
1da177e4 | 2838 | |
b1aa5301 SS |
2839 | while (path[0] == '/' && path[1] == '/') |
2840 | path++; | |
2841 | ||
02a52c5c | 2842 | sclass = unmap_class(&policy->map, orig_sclass); |
4b02b524 | 2843 | *sid = SECINITSID_UNLABELED; |
c6d3aaa4 | 2844 | |
aa8e712c | 2845 | for (genfs = policydb->genfs; genfs; genfs = genfs->next) { |
1da177e4 LT |
2846 | cmp = strcmp(fstype, genfs->fstype); |
2847 | if (cmp <= 0) | |
2848 | break; | |
2849 | } | |
2850 | ||
4b02b524 | 2851 | if (!genfs || cmp) |
cbfcd13b | 2852 | return -ENOENT; |
1da177e4 LT |
2853 | |
2854 | for (c = genfs->head; c; c = c->next) { | |
c50e125d | 2855 | size_t len = strlen(c->u.name); |
1da177e4 LT |
2856 | if ((!c->v.sclass || sclass == c->v.sclass) && |
2857 | (strncmp(c->u.name, path, len) == 0)) | |
2858 | break; | |
2859 | } | |
2860 | ||
4b02b524 | 2861 | if (!c) |
cbfcd13b | 2862 | return -ENOENT; |
1da177e4 | 2863 | |
cbfcd13b | 2864 | return ocontext_to_sid(sidtab, c, 0, sid); |
1da177e4 LT |
2865 | } |
2866 | ||
f31e7994 WL |
2867 | /** |
2868 | * security_genfs_sid - Obtain a SID for a file in a filesystem | |
2869 | * @fstype: filesystem type | |
2870 | * @path: path from root of mount | |
e9fd7292 | 2871 | * @orig_sclass: file security class |
f31e7994 WL |
2872 | * @sid: SID for path |
2873 | * | |
2874 | * Acquire policy_rwlock before calling __security_genfs_sid() and release | |
2875 | * it afterward. | |
2876 | */ | |
e67b7985 | 2877 | int security_genfs_sid(const char *fstype, |
08df4905 | 2878 | const char *path, |
f31e7994 WL |
2879 | u16 orig_sclass, |
2880 | u32 *sid) | |
2881 | { | |
1b8b31a2 | 2882 | struct selinux_policy *policy; |
f31e7994 WL |
2883 | int retval; |
2884 | ||
e67b7985 | 2885 | if (!selinux_initialized()) { |
37ea433c SS |
2886 | *sid = SECINITSID_UNLABELED; |
2887 | return 0; | |
2888 | } | |
2889 | ||
9ad6e9cb OM |
2890 | do { |
2891 | rcu_read_lock(); | |
e67b7985 | 2892 | policy = rcu_dereference(selinux_state.policy); |
9ad6e9cb OM |
2893 | retval = __security_genfs_sid(policy, fstype, path, |
2894 | orig_sclass, sid); | |
2895 | rcu_read_unlock(); | |
2896 | } while (retval == -ESTALE); | |
f31e7994 WL |
2897 | return retval; |
2898 | } | |
2899 | ||
02a52c5c SS |
2900 | int selinux_policy_genfs_sid(struct selinux_policy *policy, |
2901 | const char *fstype, | |
08df4905 | 2902 | const char *path, |
02a52c5c SS |
2903 | u16 orig_sclass, |
2904 | u32 *sid) | |
2905 | { | |
2906 | /* no lock required, policy is not yet accessible by other threads */ | |
2907 | return __security_genfs_sid(policy, fstype, path, orig_sclass, sid); | |
2908 | } | |
2909 | ||
1da177e4 LT |
2910 | /** |
2911 | * security_fs_use - Determine how to handle labeling for a filesystem. | |
a64c54cf | 2912 | * @sb: superblock in question |
1da177e4 | 2913 | */ |
e67b7985 | 2914 | int security_fs_use(struct super_block *sb) |
1da177e4 | 2915 | { |
1b8b31a2 | 2916 | struct selinux_policy *policy; |
aa8e712c | 2917 | struct policydb *policydb; |
225621c9 | 2918 | struct sidtab *sidtab; |
9ad6e9cb | 2919 | int rc; |
1da177e4 | 2920 | struct ocontext *c; |
1aea7808 | 2921 | struct superblock_security_struct *sbsec = selinux_superblock(sb); |
a64c54cf | 2922 | const char *fstype = sb->s_type->name; |
1da177e4 | 2923 | |
e67b7985 | 2924 | if (!selinux_initialized()) { |
37ea433c SS |
2925 | sbsec->behavior = SECURITY_FS_USE_NONE; |
2926 | sbsec->sid = SECINITSID_UNLABELED; | |
2927 | return 0; | |
2928 | } | |
2929 | ||
9ad6e9cb | 2930 | retry: |
1b8b31a2 | 2931 | rcu_read_lock(); |
e67b7985 | 2932 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
2933 | policydb = &policy->policydb; |
2934 | sidtab = policy->sidtab; | |
1da177e4 | 2935 | |
aa8e712c | 2936 | c = policydb->ocontexts[OCON_FSUSE]; |
4d546f81 PM |
2937 | while (c) { |
2938 | if (strcmp(fstype, c->u.name) == 0) | |
1da177e4 | 2939 | break; |
4d546f81 | 2940 | c = c->next; |
1da177e4 LT |
2941 | } |
2942 | ||
2943 | if (c) { | |
a64c54cf | 2944 | sbsec->behavior = c->v.behavior; |
cbfcd13b OM |
2945 | rc = ocontext_to_sid(sidtab, c, 0, &sbsec->sid); |
2946 | if (rc == -ESTALE) { | |
2947 | rcu_read_unlock(); | |
2948 | goto retry; | |
1da177e4 | 2949 | } |
cbfcd13b OM |
2950 | if (rc) |
2951 | goto out; | |
1da177e4 | 2952 | } else { |
1b8b31a2 | 2953 | rc = __security_genfs_sid(policy, fstype, "/", |
02a52c5c | 2954 | SECCLASS_DIR, &sbsec->sid); |
9ad6e9cb OM |
2955 | if (rc == -ESTALE) { |
2956 | rcu_read_unlock(); | |
2957 | goto retry; | |
2958 | } | |
089be43e | 2959 | if (rc) { |
a64c54cf | 2960 | sbsec->behavior = SECURITY_FS_USE_NONE; |
089be43e JM |
2961 | rc = 0; |
2962 | } else { | |
a64c54cf | 2963 | sbsec->behavior = SECURITY_FS_USE_GENFS; |
089be43e | 2964 | } |
1da177e4 LT |
2965 | } |
2966 | ||
2967 | out: | |
1b8b31a2 | 2968 | rcu_read_unlock(); |
1da177e4 LT |
2969 | return rc; |
2970 | } | |
2971 | ||
02a52c5c | 2972 | int security_get_bools(struct selinux_policy *policy, |
60abd318 | 2973 | u32 *len, char ***names, int **values) |
1da177e4 | 2974 | { |
aa8e712c | 2975 | struct policydb *policydb; |
60abd318 OM |
2976 | u32 i; |
2977 | int rc; | |
1da177e4 | 2978 | |
02a52c5c | 2979 | policydb = &policy->policydb; |
aa8e712c | 2980 | |
1da177e4 LT |
2981 | *names = NULL; |
2982 | *values = NULL; | |
2983 | ||
4b02b524 | 2984 | rc = 0; |
aa8e712c | 2985 | *len = policydb->p_bools.nprim; |
4b02b524 | 2986 | if (!*len) |
1da177e4 | 2987 | goto out; |
1da177e4 | 2988 | |
4b02b524 EP |
2989 | rc = -ENOMEM; |
2990 | *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC); | |
1da177e4 LT |
2991 | if (!*names) |
2992 | goto err; | |
1da177e4 | 2993 | |
4b02b524 EP |
2994 | rc = -ENOMEM; |
2995 | *values = kcalloc(*len, sizeof(int), GFP_ATOMIC); | |
1da177e4 LT |
2996 | if (!*values) |
2997 | goto err; | |
2998 | ||
2999 | for (i = 0; i < *len; i++) { | |
aa8e712c | 3000 | (*values)[i] = policydb->bool_val_to_struct[i]->state; |
4b02b524 EP |
3001 | |
3002 | rc = -ENOMEM; | |
aa8e712c SS |
3003 | (*names)[i] = kstrdup(sym_name(policydb, SYM_BOOLS, i), |
3004 | GFP_ATOMIC); | |
1da177e4 LT |
3005 | if (!(*names)[i]) |
3006 | goto err; | |
1da177e4 LT |
3007 | } |
3008 | rc = 0; | |
3009 | out: | |
1da177e4 LT |
3010 | return rc; |
3011 | err: | |
3012 | if (*names) { | |
3013 | for (i = 0; i < *len; i++) | |
9a5f04bf | 3014 | kfree((*names)[i]); |
65de5096 | 3015 | kfree(*names); |
1da177e4 | 3016 | } |
9a5f04bf | 3017 | kfree(*values); |
65de5096 TR |
3018 | *len = 0; |
3019 | *names = NULL; | |
3020 | *values = NULL; | |
1da177e4 LT |
3021 | goto out; |
3022 | } | |
3023 | ||
3024 | ||
e67b7985 | 3025 | int security_set_bools(u32 len, int *values) |
1da177e4 | 3026 | { |
e67b7985 | 3027 | struct selinux_state *state = &selinux_state; |
c7c556f1 | 3028 | struct selinux_policy *newpolicy, *oldpolicy; |
60abd318 | 3029 | int rc; |
c7c556f1 | 3030 | u32 i, seqno = 0; |
1da177e4 | 3031 | |
e67b7985 | 3032 | if (!selinux_initialized()) |
37ea433c SS |
3033 | return -EINVAL; |
3034 | ||
9ff9abc4 SS |
3035 | oldpolicy = rcu_dereference_protected(state->policy, |
3036 | lockdep_is_held(&state->policy_mutex)); | |
1b8b31a2 | 3037 | |
c7c556f1 | 3038 | /* Consistency check on number of booleans, should never fail */ |
1b8b31a2 | 3039 | if (WARN_ON(len != oldpolicy->policydb.p_bools.nprim)) |
c7c556f1 | 3040 | return -EINVAL; |
1da177e4 | 3041 | |
1b8b31a2 | 3042 | newpolicy = kmemdup(oldpolicy, sizeof(*newpolicy), GFP_KERNEL); |
c7c556f1 SS |
3043 | if (!newpolicy) |
3044 | return -ENOMEM; | |
3045 | ||
c7c556f1 SS |
3046 | /* |
3047 | * Deep copy only the parts of the policydb that might be | |
3048 | * modified as a result of changing booleans. | |
3049 | */ | |
3050 | rc = cond_policydb_dup(&newpolicy->policydb, &oldpolicy->policydb); | |
3051 | if (rc) { | |
3052 | kfree(newpolicy); | |
3053 | return -ENOMEM; | |
3054 | } | |
3055 | ||
3056 | /* Update the boolean states in the copy */ | |
1da177e4 | 3057 | for (i = 0; i < len; i++) { |
c7c556f1 SS |
3058 | int new_state = !!values[i]; |
3059 | int old_state = newpolicy->policydb.bool_val_to_struct[i]->state; | |
3060 | ||
3061 | if (new_state != old_state) { | |
cdfb6b34 | 3062 | audit_log(audit_context(), GFP_ATOMIC, |
af601e46 | 3063 | AUDIT_MAC_CONFIG_CHANGE, |
4746ec5b | 3064 | "bool=%s val=%d old_val=%d auid=%u ses=%u", |
c7c556f1 SS |
3065 | sym_name(&newpolicy->policydb, SYM_BOOLS, i), |
3066 | new_state, | |
3067 | old_state, | |
581abc09 | 3068 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
4746ec5b | 3069 | audit_get_sessionid(current)); |
c7c556f1 | 3070 | newpolicy->policydb.bool_val_to_struct[i]->state = new_state; |
af601e46 | 3071 | } |
1da177e4 | 3072 | } |
1da177e4 | 3073 | |
c7c556f1 SS |
3074 | /* Re-evaluate the conditional rules in the copy */ |
3075 | evaluate_cond_nodes(&newpolicy->policydb); | |
1da177e4 | 3076 | |
1b8b31a2 SS |
3077 | /* Set latest granting seqno for new policy */ |
3078 | newpolicy->latest_granting = oldpolicy->latest_granting + 1; | |
3079 | seqno = newpolicy->latest_granting; | |
3080 | ||
c7c556f1 | 3081 | /* Install the new policy */ |
1b8b31a2 | 3082 | rcu_assign_pointer(state->policy, newpolicy); |
c7c556f1 SS |
3083 | |
3084 | /* | |
3085 | * Free the conditional portions of the old policydb | |
1b8b31a2 SS |
3086 | * that were copied for the new policy, and the oldpolicy |
3087 | * structure itself but not what it references. | |
c7c556f1 | 3088 | */ |
1b8b31a2 SS |
3089 | synchronize_rcu(); |
3090 | selinux_policy_cond_free(oldpolicy); | |
c7c556f1 SS |
3091 | |
3092 | /* Notify others of the policy change */ | |
e67b7985 | 3093 | selinux_notify_policy_change(seqno); |
c7c556f1 | 3094 | return 0; |
1da177e4 LT |
3095 | } |
3096 | ||
e67b7985 | 3097 | int security_get_bool_value(u32 index) |
1da177e4 | 3098 | { |
1b8b31a2 | 3099 | struct selinux_policy *policy; |
aa8e712c | 3100 | struct policydb *policydb; |
4b02b524 | 3101 | int rc; |
60abd318 | 3102 | u32 len; |
1da177e4 | 3103 | |
e67b7985 | 3104 | if (!selinux_initialized()) |
37ea433c SS |
3105 | return 0; |
3106 | ||
1b8b31a2 | 3107 | rcu_read_lock(); |
e67b7985 | 3108 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 | 3109 | policydb = &policy->policydb; |
1da177e4 | 3110 | |
4b02b524 | 3111 | rc = -EFAULT; |
aa8e712c | 3112 | len = policydb->p_bools.nprim; |
0fd71a62 | 3113 | if (index >= len) |
1da177e4 | 3114 | goto out; |
1da177e4 | 3115 | |
aa8e712c | 3116 | rc = policydb->bool_val_to_struct[index]->state; |
1da177e4 | 3117 | out: |
1b8b31a2 | 3118 | rcu_read_unlock(); |
1da177e4 LT |
3119 | return rc; |
3120 | } | |
376bd9cb | 3121 | |
1b8b31a2 SS |
3122 | static int security_preserve_bools(struct selinux_policy *oldpolicy, |
3123 | struct selinux_policy *newpolicy) | |
e900a7d9 | 3124 | { |
60abd318 | 3125 | int rc, *bvalues = NULL; |
e900a7d9 SS |
3126 | char **bnames = NULL; |
3127 | struct cond_bool_datum *booldatum; | |
60abd318 | 3128 | u32 i, nbools = 0; |
e900a7d9 | 3129 | |
1b8b31a2 | 3130 | rc = security_get_bools(oldpolicy, &nbools, &bnames, &bvalues); |
e900a7d9 SS |
3131 | if (rc) |
3132 | goto out; | |
3133 | for (i = 0; i < nbools; i++) { | |
1b8b31a2 SS |
3134 | booldatum = symtab_search(&newpolicy->policydb.p_bools, |
3135 | bnames[i]); | |
e900a7d9 SS |
3136 | if (booldatum) |
3137 | booldatum->state = bvalues[i]; | |
3138 | } | |
1b8b31a2 | 3139 | evaluate_cond_nodes(&newpolicy->policydb); |
e900a7d9 SS |
3140 | |
3141 | out: | |
3142 | if (bnames) { | |
3143 | for (i = 0; i < nbools; i++) | |
3144 | kfree(bnames[i]); | |
3145 | } | |
3146 | kfree(bnames); | |
3147 | kfree(bvalues); | |
3148 | return rc; | |
3149 | } | |
3150 | ||
08554d6b VY |
3151 | /* |
3152 | * security_sid_mls_copy() - computes a new sid based on the given | |
3153 | * sid and the mls portion of mls_sid. | |
3154 | */ | |
e67b7985 | 3155 | int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid) |
08554d6b | 3156 | { |
1b8b31a2 | 3157 | struct selinux_policy *policy; |
46169802 SS |
3158 | struct policydb *policydb; |
3159 | struct sidtab *sidtab; | |
08554d6b VY |
3160 | struct context *context1; |
3161 | struct context *context2; | |
3162 | struct context newcon; | |
3163 | char *s; | |
3164 | u32 len; | |
4b02b524 | 3165 | int rc; |
08554d6b | 3166 | |
e67b7985 | 3167 | if (!selinux_initialized()) { |
08554d6b | 3168 | *new_sid = sid; |
9ad6e9cb | 3169 | return 0; |
08554d6b VY |
3170 | } |
3171 | ||
9ad6e9cb OM |
3172 | retry: |
3173 | rc = 0; | |
08554d6b VY |
3174 | context_init(&newcon); |
3175 | ||
1b8b31a2 | 3176 | rcu_read_lock(); |
e67b7985 | 3177 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3178 | policydb = &policy->policydb; |
3179 | sidtab = policy->sidtab; | |
46169802 SS |
3180 | |
3181 | if (!policydb->mls_enabled) { | |
3182 | *new_sid = sid; | |
3183 | goto out_unlock; | |
3184 | } | |
3185 | ||
4b02b524 | 3186 | rc = -EINVAL; |
aa8e712c | 3187 | context1 = sidtab_search(sidtab, sid); |
08554d6b | 3188 | if (!context1) { |
b54c85c1 | 3189 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 3190 | __func__, sid); |
08554d6b VY |
3191 | goto out_unlock; |
3192 | } | |
3193 | ||
4b02b524 | 3194 | rc = -EINVAL; |
aa8e712c | 3195 | context2 = sidtab_search(sidtab, mls_sid); |
08554d6b | 3196 | if (!context2) { |
b54c85c1 | 3197 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 3198 | __func__, mls_sid); |
08554d6b VY |
3199 | goto out_unlock; |
3200 | } | |
3201 | ||
3202 | newcon.user = context1->user; | |
3203 | newcon.role = context1->role; | |
3204 | newcon.type = context1->type; | |
0efc61ea | 3205 | rc = mls_context_cpy(&newcon, context2); |
08554d6b VY |
3206 | if (rc) |
3207 | goto out_unlock; | |
3208 | ||
08554d6b | 3209 | /* Check the validity of the new context. */ |
aa8e712c | 3210 | if (!policydb_context_isvalid(policydb, &newcon)) { |
e67b7985 | 3211 | rc = convert_context_handle_invalid_context(policydb, |
1b8b31a2 | 3212 | &newcon); |
4b02b524 | 3213 | if (rc) { |
aa8e712c SS |
3214 | if (!context_struct_to_string(policydb, &newcon, &s, |
3215 | &len)) { | |
ea74a685 RGB |
3216 | struct audit_buffer *ab; |
3217 | ||
3218 | ab = audit_log_start(audit_context(), | |
3219 | GFP_ATOMIC, | |
3220 | AUDIT_SELINUX_ERR); | |
3221 | audit_log_format(ab, | |
3222 | "op=security_sid_mls_copy invalid_context="); | |
3223 | /* don't record NUL with untrusted strings */ | |
3224 | audit_log_n_untrustedstring(ab, s, len - 1); | |
3225 | audit_log_end(ab); | |
4b02b524 EP |
3226 | kfree(s); |
3227 | } | |
3228 | goto out_unlock; | |
3229 | } | |
08554d6b | 3230 | } |
225621c9 | 3231 | rc = sidtab_context_to_sid(sidtab, &newcon, new_sid); |
9ad6e9cb OM |
3232 | if (rc == -ESTALE) { |
3233 | rcu_read_unlock(); | |
3234 | context_destroy(&newcon); | |
3235 | goto retry; | |
3236 | } | |
08554d6b | 3237 | out_unlock: |
1b8b31a2 | 3238 | rcu_read_unlock(); |
08554d6b | 3239 | context_destroy(&newcon); |
08554d6b VY |
3240 | return rc; |
3241 | } | |
3242 | ||
220deb96 PM |
3243 | /** |
3244 | * security_net_peersid_resolve - Compare and resolve two network peer SIDs | |
3245 | * @nlbl_sid: NetLabel SID | |
3246 | * @nlbl_type: NetLabel labeling protocol type | |
3247 | * @xfrm_sid: XFRM SID | |
e9fd7292 | 3248 | * @peer_sid: network peer sid |
220deb96 PM |
3249 | * |
3250 | * Description: | |
3251 | * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be | |
3252 | * resolved into a single SID it is returned via @peer_sid and the function | |
3253 | * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function | |
3254 | * returns a negative value. A table summarizing the behavior is below: | |
3255 | * | |
3256 | * | function return | @sid | |
3257 | * ------------------------------+-----------------+----------------- | |
3258 | * no peer labels | 0 | SECSID_NULL | |
3259 | * single peer label | 0 | <peer_label> | |
3260 | * multiple, consistent labels | 0 | <peer_label> | |
3261 | * multiple, inconsistent labels | -<errno> | SECSID_NULL | |
3262 | * | |
3263 | */ | |
e67b7985 | 3264 | int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, |
220deb96 PM |
3265 | u32 xfrm_sid, |
3266 | u32 *peer_sid) | |
3267 | { | |
1b8b31a2 | 3268 | struct selinux_policy *policy; |
46169802 SS |
3269 | struct policydb *policydb; |
3270 | struct sidtab *sidtab; | |
220deb96 PM |
3271 | int rc; |
3272 | struct context *nlbl_ctx; | |
3273 | struct context *xfrm_ctx; | |
3274 | ||
4b02b524 EP |
3275 | *peer_sid = SECSID_NULL; |
3276 | ||
220deb96 PM |
3277 | /* handle the common (which also happens to be the set of easy) cases |
3278 | * right away, these two if statements catch everything involving a | |
3279 | * single or absent peer SID/label */ | |
3280 | if (xfrm_sid == SECSID_NULL) { | |
3281 | *peer_sid = nlbl_sid; | |
3282 | return 0; | |
3283 | } | |
3284 | /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label | |
3285 | * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label | |
3286 | * is present */ | |
3287 | if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) { | |
3288 | *peer_sid = xfrm_sid; | |
3289 | return 0; | |
3290 | } | |
3291 | ||
e67b7985 | 3292 | if (!selinux_initialized()) |
37ea433c SS |
3293 | return 0; |
3294 | ||
1b8b31a2 | 3295 | rcu_read_lock(); |
e67b7985 | 3296 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3297 | policydb = &policy->policydb; |
3298 | sidtab = policy->sidtab; | |
46169802 | 3299 | |
aa8e712c SS |
3300 | /* |
3301 | * We don't need to check initialized here since the only way both | |
220deb96 | 3302 | * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the |
aa8e712c SS |
3303 | * security server was initialized and state->initialized was true. |
3304 | */ | |
46169802 SS |
3305 | if (!policydb->mls_enabled) { |
3306 | rc = 0; | |
3307 | goto out; | |
3308 | } | |
220deb96 | 3309 | |
4b02b524 | 3310 | rc = -EINVAL; |
aa8e712c | 3311 | nlbl_ctx = sidtab_search(sidtab, nlbl_sid); |
220deb96 | 3312 | if (!nlbl_ctx) { |
b54c85c1 | 3313 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 3314 | __func__, nlbl_sid); |
4b02b524 | 3315 | goto out; |
220deb96 | 3316 | } |
4b02b524 | 3317 | rc = -EINVAL; |
aa8e712c | 3318 | xfrm_ctx = sidtab_search(sidtab, xfrm_sid); |
220deb96 | 3319 | if (!xfrm_ctx) { |
b54c85c1 | 3320 | pr_err("SELinux: %s: unrecognized SID %d\n", |
744ba35e | 3321 | __func__, xfrm_sid); |
4b02b524 | 3322 | goto out; |
220deb96 PM |
3323 | } |
3324 | rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES); | |
4b02b524 EP |
3325 | if (rc) |
3326 | goto out; | |
220deb96 | 3327 | |
4b02b524 EP |
3328 | /* at present NetLabel SIDs/labels really only carry MLS |
3329 | * information so if the MLS portion of the NetLabel SID | |
3330 | * matches the MLS portion of the labeled XFRM SID/label | |
3331 | * then pass along the XFRM SID as it is the most | |
3332 | * expressive */ | |
3333 | *peer_sid = xfrm_sid; | |
3334 | out: | |
1b8b31a2 | 3335 | rcu_read_unlock(); |
220deb96 PM |
3336 | return rc; |
3337 | } | |
3338 | ||
55fcf09b CP |
3339 | static int get_classes_callback(void *k, void *d, void *args) |
3340 | { | |
3341 | struct class_datum *datum = d; | |
3342 | char *name = k, **classes = args; | |
c50e125d | 3343 | u32 value = datum->value - 1; |
55fcf09b CP |
3344 | |
3345 | classes[value] = kstrdup(name, GFP_ATOMIC); | |
3346 | if (!classes[value]) | |
3347 | return -ENOMEM; | |
3348 | ||
3349 | return 0; | |
3350 | } | |
3351 | ||
02a52c5c | 3352 | int security_get_classes(struct selinux_policy *policy, |
c50e125d | 3353 | char ***classes, u32 *nclasses) |
55fcf09b | 3354 | { |
46169802 | 3355 | struct policydb *policydb; |
4b02b524 | 3356 | int rc; |
55fcf09b | 3357 | |
02a52c5c | 3358 | policydb = &policy->policydb; |
46169802 | 3359 | |
4b02b524 | 3360 | rc = -ENOMEM; |
aa8e712c | 3361 | *nclasses = policydb->p_classes.nprim; |
9f59f90b | 3362 | *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC); |
55fcf09b CP |
3363 | if (!*classes) |
3364 | goto out; | |
3365 | ||
03414a49 OM |
3366 | rc = hashtab_map(&policydb->p_classes.table, get_classes_callback, |
3367 | *classes); | |
4b02b524 | 3368 | if (rc) { |
c50e125d CG |
3369 | u32 i; |
3370 | ||
55fcf09b CP |
3371 | for (i = 0; i < *nclasses; i++) |
3372 | kfree((*classes)[i]); | |
3373 | kfree(*classes); | |
3374 | } | |
3375 | ||
3376 | out: | |
55fcf09b CP |
3377 | return rc; |
3378 | } | |
3379 | ||
3380 | static int get_permissions_callback(void *k, void *d, void *args) | |
3381 | { | |
3382 | struct perm_datum *datum = d; | |
3383 | char *name = k, **perms = args; | |
c50e125d | 3384 | u32 value = datum->value - 1; |
55fcf09b CP |
3385 | |
3386 | perms[value] = kstrdup(name, GFP_ATOMIC); | |
3387 | if (!perms[value]) | |
3388 | return -ENOMEM; | |
3389 | ||
3390 | return 0; | |
3391 | } | |
3392 | ||
02a52c5c | 3393 | int security_get_permissions(struct selinux_policy *policy, |
c50e125d | 3394 | const char *class, char ***perms, u32 *nperms) |
55fcf09b | 3395 | { |
46169802 | 3396 | struct policydb *policydb; |
c50e125d CG |
3397 | u32 i; |
3398 | int rc; | |
55fcf09b CP |
3399 | struct class_datum *match; |
3400 | ||
02a52c5c | 3401 | policydb = &policy->policydb; |
46169802 | 3402 | |
4b02b524 | 3403 | rc = -EINVAL; |
237389e3 | 3404 | match = symtab_search(&policydb->p_classes, class); |
55fcf09b | 3405 | if (!match) { |
b54c85c1 | 3406 | pr_err("SELinux: %s: unrecognized class %s\n", |
dd6f953a | 3407 | __func__, class); |
55fcf09b CP |
3408 | goto out; |
3409 | } | |
3410 | ||
4b02b524 | 3411 | rc = -ENOMEM; |
55fcf09b | 3412 | *nperms = match->permissions.nprim; |
9f59f90b | 3413 | *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC); |
55fcf09b CP |
3414 | if (!*perms) |
3415 | goto out; | |
3416 | ||
3417 | if (match->comdatum) { | |
03414a49 OM |
3418 | rc = hashtab_map(&match->comdatum->permissions.table, |
3419 | get_permissions_callback, *perms); | |
4b02b524 | 3420 | if (rc) |
55fcf09b CP |
3421 | goto err; |
3422 | } | |
3423 | ||
03414a49 OM |
3424 | rc = hashtab_map(&match->permissions.table, get_permissions_callback, |
3425 | *perms); | |
4b02b524 | 3426 | if (rc) |
55fcf09b CP |
3427 | goto err; |
3428 | ||
3429 | out: | |
55fcf09b CP |
3430 | return rc; |
3431 | ||
3432 | err: | |
55fcf09b CP |
3433 | for (i = 0; i < *nperms; i++) |
3434 | kfree((*perms)[i]); | |
3435 | kfree(*perms); | |
3436 | return rc; | |
3437 | } | |
3438 | ||
e67b7985 | 3439 | int security_get_reject_unknown(void) |
3f12070e | 3440 | { |
1b8b31a2 | 3441 | struct selinux_policy *policy; |
46169802 SS |
3442 | int value; |
3443 | ||
e67b7985 | 3444 | if (!selinux_initialized()) |
37ea433c SS |
3445 | return 0; |
3446 | ||
1b8b31a2 | 3447 | rcu_read_lock(); |
e67b7985 | 3448 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3449 | value = policy->policydb.reject_unknown; |
3450 | rcu_read_unlock(); | |
46169802 | 3451 | return value; |
3f12070e EP |
3452 | } |
3453 | ||
e67b7985 | 3454 | int security_get_allow_unknown(void) |
3f12070e | 3455 | { |
1b8b31a2 | 3456 | struct selinux_policy *policy; |
46169802 SS |
3457 | int value; |
3458 | ||
e67b7985 | 3459 | if (!selinux_initialized()) |
37ea433c SS |
3460 | return 0; |
3461 | ||
1b8b31a2 | 3462 | rcu_read_lock(); |
e67b7985 | 3463 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3464 | value = policy->policydb.allow_unknown; |
3465 | rcu_read_unlock(); | |
46169802 | 3466 | return value; |
3f12070e EP |
3467 | } |
3468 | ||
3bb56b25 PM |
3469 | /** |
3470 | * security_policycap_supported - Check for a specific policy capability | |
3471 | * @req_cap: capability | |
3472 | * | |
3473 | * Description: | |
3474 | * This function queries the currently loaded policy to see if it supports the | |
3475 | * capability specified by @req_cap. Returns true (1) if the capability is | |
3476 | * supported, false (0) if it isn't supported. | |
3477 | * | |
3478 | */ | |
e67b7985 | 3479 | int security_policycap_supported(unsigned int req_cap) |
3bb56b25 | 3480 | { |
1b8b31a2 | 3481 | struct selinux_policy *policy; |
3bb56b25 PM |
3482 | int rc; |
3483 | ||
e67b7985 | 3484 | if (!selinux_initialized()) |
37ea433c SS |
3485 | return 0; |
3486 | ||
1b8b31a2 | 3487 | rcu_read_lock(); |
e67b7985 | 3488 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3489 | rc = ebitmap_get_bit(&policy->policydb.policycaps, req_cap); |
3490 | rcu_read_unlock(); | |
3bb56b25 PM |
3491 | |
3492 | return rc; | |
3493 | } | |
3494 | ||
376bd9cb DG |
3495 | struct selinux_audit_rule { |
3496 | u32 au_seqno; | |
3497 | struct context au_ctxt; | |
3498 | }; | |
3499 | ||
9d57a7f9 | 3500 | void selinux_audit_rule_free(void *vrule) |
376bd9cb | 3501 | { |
9d57a7f9 AD |
3502 | struct selinux_audit_rule *rule = vrule; |
3503 | ||
376bd9cb DG |
3504 | if (rule) { |
3505 | context_destroy(&rule->au_ctxt); | |
3506 | kfree(rule); | |
3507 | } | |
3508 | } | |
3509 | ||
9d57a7f9 | 3510 | int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) |
376bd9cb | 3511 | { |
aa8e712c | 3512 | struct selinux_state *state = &selinux_state; |
1b8b31a2 | 3513 | struct selinux_policy *policy; |
46169802 | 3514 | struct policydb *policydb; |
376bd9cb DG |
3515 | struct selinux_audit_rule *tmprule; |
3516 | struct role_datum *roledatum; | |
3517 | struct type_datum *typedatum; | |
3518 | struct user_datum *userdatum; | |
9d57a7f9 | 3519 | struct selinux_audit_rule **rule = (struct selinux_audit_rule **)vrule; |
376bd9cb DG |
3520 | int rc = 0; |
3521 | ||
3522 | *rule = NULL; | |
3523 | ||
e67b7985 | 3524 | if (!selinux_initialized()) |
3ad40d64 | 3525 | return -EOPNOTSUPP; |
376bd9cb DG |
3526 | |
3527 | switch (field) { | |
3a6b9f85 DG |
3528 | case AUDIT_SUBJ_USER: |
3529 | case AUDIT_SUBJ_ROLE: | |
3530 | case AUDIT_SUBJ_TYPE: | |
6e5a2d1d DG |
3531 | case AUDIT_OBJ_USER: |
3532 | case AUDIT_OBJ_ROLE: | |
3533 | case AUDIT_OBJ_TYPE: | |
376bd9cb | 3534 | /* only 'equals' and 'not equals' fit user, role, and type */ |
5af75d8d | 3535 | if (op != Audit_equal && op != Audit_not_equal) |
376bd9cb DG |
3536 | return -EINVAL; |
3537 | break; | |
3a6b9f85 DG |
3538 | case AUDIT_SUBJ_SEN: |
3539 | case AUDIT_SUBJ_CLR: | |
6e5a2d1d DG |
3540 | case AUDIT_OBJ_LEV_LOW: |
3541 | case AUDIT_OBJ_LEV_HIGH: | |
25985edc | 3542 | /* we do not allow a range, indicated by the presence of '-' */ |
376bd9cb DG |
3543 | if (strchr(rulestr, '-')) |
3544 | return -EINVAL; | |
3545 | break; | |
3546 | default: | |
3547 | /* only the above fields are valid */ | |
3548 | return -EINVAL; | |
3549 | } | |
3550 | ||
3551 | tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); | |
3552 | if (!tmprule) | |
3553 | return -ENOMEM; | |
376bd9cb DG |
3554 | context_init(&tmprule->au_ctxt); |
3555 | ||
1b8b31a2 SS |
3556 | rcu_read_lock(); |
3557 | policy = rcu_dereference(state->policy); | |
3558 | policydb = &policy->policydb; | |
1b8b31a2 | 3559 | tmprule->au_seqno = policy->latest_granting; |
376bd9cb | 3560 | switch (field) { |
3a6b9f85 | 3561 | case AUDIT_SUBJ_USER: |
6e5a2d1d | 3562 | case AUDIT_OBJ_USER: |
237389e3 | 3563 | userdatum = symtab_search(&policydb->p_users, rulestr); |
c52df19e PM |
3564 | if (!userdatum) { |
3565 | rc = -EINVAL; | |
3566 | goto err; | |
3567 | } | |
4b02b524 | 3568 | tmprule->au_ctxt.user = userdatum->value; |
376bd9cb | 3569 | break; |
3a6b9f85 | 3570 | case AUDIT_SUBJ_ROLE: |
6e5a2d1d | 3571 | case AUDIT_OBJ_ROLE: |
237389e3 | 3572 | roledatum = symtab_search(&policydb->p_roles, rulestr); |
c52df19e PM |
3573 | if (!roledatum) { |
3574 | rc = -EINVAL; | |
3575 | goto err; | |
3576 | } | |
4b02b524 | 3577 | tmprule->au_ctxt.role = roledatum->value; |
376bd9cb | 3578 | break; |
3a6b9f85 | 3579 | case AUDIT_SUBJ_TYPE: |
6e5a2d1d | 3580 | case AUDIT_OBJ_TYPE: |
237389e3 | 3581 | typedatum = symtab_search(&policydb->p_types, rulestr); |
c52df19e PM |
3582 | if (!typedatum) { |
3583 | rc = -EINVAL; | |
3584 | goto err; | |
3585 | } | |
4b02b524 | 3586 | tmprule->au_ctxt.type = typedatum->value; |
376bd9cb | 3587 | break; |
3a6b9f85 DG |
3588 | case AUDIT_SUBJ_SEN: |
3589 | case AUDIT_SUBJ_CLR: | |
6e5a2d1d DG |
3590 | case AUDIT_OBJ_LEV_LOW: |
3591 | case AUDIT_OBJ_LEV_HIGH: | |
aa8e712c SS |
3592 | rc = mls_from_string(policydb, rulestr, &tmprule->au_ctxt, |
3593 | GFP_ATOMIC); | |
4b02b524 | 3594 | if (rc) |
c52df19e | 3595 | goto err; |
376bd9cb DG |
3596 | break; |
3597 | } | |
1b8b31a2 | 3598 | rcu_read_unlock(); |
376bd9cb | 3599 | |
376bd9cb | 3600 | *rule = tmprule; |
c52df19e | 3601 | return 0; |
376bd9cb | 3602 | |
c52df19e PM |
3603 | err: |
3604 | rcu_read_unlock(); | |
3605 | selinux_audit_rule_free(tmprule); | |
3606 | *rule = NULL; | |
376bd9cb DG |
3607 | return rc; |
3608 | } | |
3609 | ||
9d57a7f9 AD |
3610 | /* Check to see if the rule contains any selinux fields */ |
3611 | int selinux_audit_rule_known(struct audit_krule *rule) | |
3612 | { | |
c50e125d | 3613 | u32 i; |
9d57a7f9 AD |
3614 | |
3615 | for (i = 0; i < rule->field_count; i++) { | |
3616 | struct audit_field *f = &rule->fields[i]; | |
3617 | switch (f->type) { | |
3618 | case AUDIT_SUBJ_USER: | |
3619 | case AUDIT_SUBJ_ROLE: | |
3620 | case AUDIT_SUBJ_TYPE: | |
3621 | case AUDIT_SUBJ_SEN: | |
3622 | case AUDIT_SUBJ_CLR: | |
3623 | case AUDIT_OBJ_USER: | |
3624 | case AUDIT_OBJ_ROLE: | |
3625 | case AUDIT_OBJ_TYPE: | |
3626 | case AUDIT_OBJ_LEV_LOW: | |
3627 | case AUDIT_OBJ_LEV_HIGH: | |
3628 | return 1; | |
3629 | } | |
3630 | } | |
3631 | ||
3632 | return 0; | |
3633 | } | |
3634 | ||
90462a5b | 3635 | int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) |
376bd9cb | 3636 | { |
aa8e712c | 3637 | struct selinux_state *state = &selinux_state; |
1b8b31a2 | 3638 | struct selinux_policy *policy; |
376bd9cb DG |
3639 | struct context *ctxt; |
3640 | struct mls_level *level; | |
9d57a7f9 | 3641 | struct selinux_audit_rule *rule = vrule; |
376bd9cb DG |
3642 | int match = 0; |
3643 | ||
9ad42a79 RGB |
3644 | if (unlikely(!rule)) { |
3645 | WARN_ONCE(1, "selinux_audit_rule_match: missing rule\n"); | |
376bd9cb DG |
3646 | return -ENOENT; |
3647 | } | |
3648 | ||
e67b7985 | 3649 | if (!selinux_initialized()) |
37ea433c SS |
3650 | return 0; |
3651 | ||
1b8b31a2 SS |
3652 | rcu_read_lock(); |
3653 | ||
3654 | policy = rcu_dereference(state->policy); | |
376bd9cb | 3655 | |
1b8b31a2 | 3656 | if (rule->au_seqno < policy->latest_granting) { |
376bd9cb DG |
3657 | match = -ESTALE; |
3658 | goto out; | |
3659 | } | |
3660 | ||
1b8b31a2 | 3661 | ctxt = sidtab_search(policy->sidtab, sid); |
9ad42a79 RGB |
3662 | if (unlikely(!ctxt)) { |
3663 | WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n", | |
5d55a345 | 3664 | sid); |
376bd9cb DG |
3665 | match = -ENOENT; |
3666 | goto out; | |
3667 | } | |
3668 | ||
3669 | /* a field/op pair that is not caught here will simply fall through | |
3670 | without a match */ | |
3671 | switch (field) { | |
3a6b9f85 | 3672 | case AUDIT_SUBJ_USER: |
6e5a2d1d | 3673 | case AUDIT_OBJ_USER: |
376bd9cb | 3674 | switch (op) { |
5af75d8d | 3675 | case Audit_equal: |
376bd9cb DG |
3676 | match = (ctxt->user == rule->au_ctxt.user); |
3677 | break; | |
5af75d8d | 3678 | case Audit_not_equal: |
376bd9cb DG |
3679 | match = (ctxt->user != rule->au_ctxt.user); |
3680 | break; | |
3681 | } | |
3682 | break; | |
3a6b9f85 | 3683 | case AUDIT_SUBJ_ROLE: |
6e5a2d1d | 3684 | case AUDIT_OBJ_ROLE: |
376bd9cb | 3685 | switch (op) { |
5af75d8d | 3686 | case Audit_equal: |
376bd9cb DG |
3687 | match = (ctxt->role == rule->au_ctxt.role); |
3688 | break; | |
5af75d8d | 3689 | case Audit_not_equal: |
376bd9cb DG |
3690 | match = (ctxt->role != rule->au_ctxt.role); |
3691 | break; | |
3692 | } | |
3693 | break; | |
3a6b9f85 | 3694 | case AUDIT_SUBJ_TYPE: |
6e5a2d1d | 3695 | case AUDIT_OBJ_TYPE: |
376bd9cb | 3696 | switch (op) { |
5af75d8d | 3697 | case Audit_equal: |
376bd9cb DG |
3698 | match = (ctxt->type == rule->au_ctxt.type); |
3699 | break; | |
5af75d8d | 3700 | case Audit_not_equal: |
376bd9cb DG |
3701 | match = (ctxt->type != rule->au_ctxt.type); |
3702 | break; | |
3703 | } | |
3704 | break; | |
3a6b9f85 DG |
3705 | case AUDIT_SUBJ_SEN: |
3706 | case AUDIT_SUBJ_CLR: | |
6e5a2d1d DG |
3707 | case AUDIT_OBJ_LEV_LOW: |
3708 | case AUDIT_OBJ_LEV_HIGH: | |
3709 | level = ((field == AUDIT_SUBJ_SEN || | |
5d55a345 EP |
3710 | field == AUDIT_OBJ_LEV_LOW) ? |
3711 | &ctxt->range.level[0] : &ctxt->range.level[1]); | |
376bd9cb | 3712 | switch (op) { |
5af75d8d | 3713 | case Audit_equal: |
376bd9cb | 3714 | match = mls_level_eq(&rule->au_ctxt.range.level[0], |
5d55a345 | 3715 | level); |
376bd9cb | 3716 | break; |
5af75d8d | 3717 | case Audit_not_equal: |
376bd9cb | 3718 | match = !mls_level_eq(&rule->au_ctxt.range.level[0], |
5d55a345 | 3719 | level); |
376bd9cb | 3720 | break; |
5af75d8d | 3721 | case Audit_lt: |
376bd9cb | 3722 | match = (mls_level_dom(&rule->au_ctxt.range.level[0], |
5d55a345 EP |
3723 | level) && |
3724 | !mls_level_eq(&rule->au_ctxt.range.level[0], | |
3725 | level)); | |
376bd9cb | 3726 | break; |
5af75d8d | 3727 | case Audit_le: |
376bd9cb | 3728 | match = mls_level_dom(&rule->au_ctxt.range.level[0], |
5d55a345 | 3729 | level); |
376bd9cb | 3730 | break; |
5af75d8d | 3731 | case Audit_gt: |
376bd9cb | 3732 | match = (mls_level_dom(level, |
5d55a345 EP |
3733 | &rule->au_ctxt.range.level[0]) && |
3734 | !mls_level_eq(level, | |
3735 | &rule->au_ctxt.range.level[0])); | |
376bd9cb | 3736 | break; |
5af75d8d | 3737 | case Audit_ge: |
376bd9cb | 3738 | match = mls_level_dom(level, |
5d55a345 | 3739 | &rule->au_ctxt.range.level[0]); |
376bd9cb DG |
3740 | break; |
3741 | } | |
3742 | } | |
3743 | ||
3744 | out: | |
1b8b31a2 | 3745 | rcu_read_unlock(); |
376bd9cb DG |
3746 | return match; |
3747 | } | |
3748 | ||
562c99f2 | 3749 | static int aurule_avc_callback(u32 event) |
376bd9cb | 3750 | { |
3c797e51 OM |
3751 | if (event == AVC_CALLBACK_RESET) |
3752 | return audit_update_lsm_rules(); | |
3753 | return 0; | |
376bd9cb DG |
3754 | } |
3755 | ||
3756 | static int __init aurule_init(void) | |
3757 | { | |
3758 | int err; | |
3759 | ||
562c99f2 | 3760 | err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET); |
376bd9cb DG |
3761 | if (err) |
3762 | panic("avc_add_callback() failed, error %d\n", err); | |
3763 | ||
3764 | return err; | |
3765 | } | |
3766 | __initcall(aurule_init); | |
3767 | ||
7420ed23 | 3768 | #ifdef CONFIG_NETLABEL |
7420ed23 | 3769 | /** |
5778eabd PM |
3770 | * security_netlbl_cache_add - Add an entry to the NetLabel cache |
3771 | * @secattr: the NetLabel packet security attributes | |
5dbe1eb0 | 3772 | * @sid: the SELinux SID |
7420ed23 VY |
3773 | * |
3774 | * Description: | |
3775 | * Attempt to cache the context in @ctx, which was derived from the packet in | |
5778eabd PM |
3776 | * @skb, in the NetLabel subsystem cache. This function assumes @secattr has |
3777 | * already been initialized. | |
7420ed23 VY |
3778 | * |
3779 | */ | |
5778eabd | 3780 | static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr, |
5dbe1eb0 | 3781 | u32 sid) |
7420ed23 | 3782 | { |
5dbe1eb0 | 3783 | u32 *sid_cache; |
7420ed23 | 3784 | |
5dbe1eb0 PM |
3785 | sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC); |
3786 | if (sid_cache == NULL) | |
5778eabd | 3787 | return; |
5dbe1eb0 PM |
3788 | secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC); |
3789 | if (secattr->cache == NULL) { | |
3790 | kfree(sid_cache); | |
5778eabd | 3791 | return; |
0ec8abd7 | 3792 | } |
7420ed23 | 3793 | |
5dbe1eb0 PM |
3794 | *sid_cache = sid; |
3795 | secattr->cache->free = kfree; | |
3796 | secattr->cache->data = sid_cache; | |
5778eabd | 3797 | secattr->flags |= NETLBL_SECATTR_CACHE; |
7420ed23 VY |
3798 | } |
3799 | ||
3800 | /** | |
5778eabd | 3801 | * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID |
7420ed23 | 3802 | * @secattr: the NetLabel packet security attributes |
7420ed23 VY |
3803 | * @sid: the SELinux SID |
3804 | * | |
3805 | * Description: | |
5778eabd | 3806 | * Convert the given NetLabel security attributes in @secattr into a |
7420ed23 | 3807 | * SELinux SID. If the @secattr field does not contain a full SELinux |
25985edc | 3808 | * SID/context then use SECINITSID_NETMSG as the foundation. If possible the |
5dbe1eb0 PM |
3809 | * 'cache' field of @secattr is set and the CACHE flag is set; this is to |
3810 | * allow the @secattr to be used by NetLabel to cache the secattr to SID | |
3811 | * conversion for future lookups. Returns zero on success, negative values on | |
3812 | * failure. | |
7420ed23 VY |
3813 | * |
3814 | */ | |
e67b7985 | 3815 | int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, |
5778eabd | 3816 | u32 *sid) |
7420ed23 | 3817 | { |
1b8b31a2 | 3818 | struct selinux_policy *policy; |
46169802 SS |
3819 | struct policydb *policydb; |
3820 | struct sidtab *sidtab; | |
7ae9f23c | 3821 | int rc; |
7420ed23 VY |
3822 | struct context *ctx; |
3823 | struct context ctx_new; | |
5778eabd | 3824 | |
e67b7985 | 3825 | if (!selinux_initialized()) { |
5778eabd PM |
3826 | *sid = SECSID_NULL; |
3827 | return 0; | |
3828 | } | |
7420ed23 | 3829 | |
9ad6e9cb OM |
3830 | retry: |
3831 | rc = 0; | |
1b8b31a2 | 3832 | rcu_read_lock(); |
e67b7985 | 3833 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 SS |
3834 | policydb = &policy->policydb; |
3835 | sidtab = policy->sidtab; | |
46169802 | 3836 | |
7ae9f23c | 3837 | if (secattr->flags & NETLBL_SECATTR_CACHE) |
5dbe1eb0 | 3838 | *sid = *(u32 *)secattr->cache->data; |
7ae9f23c | 3839 | else if (secattr->flags & NETLBL_SECATTR_SECID) |
16efd454 | 3840 | *sid = secattr->attr.secid; |
7ae9f23c EP |
3841 | else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) { |
3842 | rc = -EIDRM; | |
aa8e712c | 3843 | ctx = sidtab_search(sidtab, SECINITSID_NETMSG); |
7420ed23 | 3844 | if (ctx == NULL) |
7ae9f23c | 3845 | goto out; |
7420ed23 | 3846 | |
81990fbd | 3847 | context_init(&ctx_new); |
7420ed23 VY |
3848 | ctx_new.user = ctx->user; |
3849 | ctx_new.role = ctx->role; | |
3850 | ctx_new.type = ctx->type; | |
aa8e712c | 3851 | mls_import_netlbl_lvl(policydb, &ctx_new, secattr); |
701a90ba | 3852 | if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { |
aa8e712c | 3853 | rc = mls_import_netlbl_cat(policydb, &ctx_new, secattr); |
7ae9f23c EP |
3854 | if (rc) |
3855 | goto out; | |
7420ed23 | 3856 | } |
7ae9f23c | 3857 | rc = -EIDRM; |
9ad6e9cb OM |
3858 | if (!mls_context_isvalid(policydb, &ctx_new)) { |
3859 | ebitmap_destroy(&ctx_new.range.level[0].cat); | |
3860 | goto out; | |
3861 | } | |
7420ed23 | 3862 | |
225621c9 | 3863 | rc = sidtab_context_to_sid(sidtab, &ctx_new, sid); |
9ad6e9cb OM |
3864 | ebitmap_destroy(&ctx_new.range.level[0].cat); |
3865 | if (rc == -ESTALE) { | |
3866 | rcu_read_unlock(); | |
3867 | goto retry; | |
3868 | } | |
7ae9f23c | 3869 | if (rc) |
9ad6e9cb | 3870 | goto out; |
7420ed23 | 3871 | |
5dbe1eb0 | 3872 | security_netlbl_cache_add(secattr, *sid); |
7ae9f23c | 3873 | } else |
388b2405 | 3874 | *sid = SECSID_NULL; |
7420ed23 | 3875 | |
7ae9f23c | 3876 | out: |
1b8b31a2 | 3877 | rcu_read_unlock(); |
7ae9f23c | 3878 | return rc; |
7420ed23 VY |
3879 | } |
3880 | ||
3881 | /** | |
5778eabd PM |
3882 | * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr |
3883 | * @sid: the SELinux SID | |
3884 | * @secattr: the NetLabel packet security attributes | |
7420ed23 VY |
3885 | * |
3886 | * Description: | |
5778eabd PM |
3887 | * Convert the given SELinux SID in @sid into a NetLabel security attribute. |
3888 | * Returns zero on success, negative values on failure. | |
7420ed23 VY |
3889 | * |
3890 | */ | |
e67b7985 | 3891 | int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) |
7420ed23 | 3892 | { |
1b8b31a2 | 3893 | struct selinux_policy *policy; |
46169802 | 3894 | struct policydb *policydb; |
99d854d2 | 3895 | int rc; |
7420ed23 VY |
3896 | struct context *ctx; |
3897 | ||
e67b7985 | 3898 | if (!selinux_initialized()) |
7420ed23 VY |
3899 | return 0; |
3900 | ||
1b8b31a2 | 3901 | rcu_read_lock(); |
e67b7985 | 3902 | policy = rcu_dereference(selinux_state.policy); |
1b8b31a2 | 3903 | policydb = &policy->policydb; |
46169802 | 3904 | |
4b02b524 | 3905 | rc = -ENOENT; |
1b8b31a2 | 3906 | ctx = sidtab_search(policy->sidtab, sid); |
4b02b524 EP |
3907 | if (ctx == NULL) |
3908 | goto out; | |
3909 | ||
3910 | rc = -ENOMEM; | |
aa8e712c | 3911 | secattr->domain = kstrdup(sym_name(policydb, SYM_TYPES, ctx->type - 1), |
5778eabd | 3912 | GFP_ATOMIC); |
4b02b524 EP |
3913 | if (secattr->domain == NULL) |
3914 | goto out; | |
3915 | ||
8d75899d PM |
3916 | secattr->attr.secid = sid; |
3917 | secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID; | |
aa8e712c SS |
3918 | mls_export_netlbl_lvl(policydb, ctx, secattr); |
3919 | rc = mls_export_netlbl_cat(policydb, ctx, secattr); | |
4b02b524 | 3920 | out: |
1b8b31a2 | 3921 | rcu_read_unlock(); |
f8687afe PM |
3922 | return rc; |
3923 | } | |
7420ed23 | 3924 | #endif /* CONFIG_NETLABEL */ |
cee74f47 | 3925 | |
fdd1ffe8 LR |
3926 | /** |
3927 | * __security_read_policy - read the policy. | |
3928 | * @policy: SELinux policy | |
3929 | * @data: binary policy data | |
3930 | * @len: length of data in bytes | |
3931 | * | |
3932 | */ | |
3933 | static int __security_read_policy(struct selinux_policy *policy, | |
3934 | void *data, size_t *len) | |
3935 | { | |
3936 | int rc; | |
3937 | struct policy_file fp; | |
3938 | ||
3939 | fp.data = data; | |
3940 | fp.len = *len; | |
3941 | ||
3942 | rc = policydb_write(&policy->policydb, &fp); | |
3943 | if (rc) | |
3944 | return rc; | |
3945 | ||
3946 | *len = (unsigned long)fp.data - (unsigned long)data; | |
3947 | return 0; | |
3948 | } | |
3949 | ||
cee74f47 EP |
3950 | /** |
3951 | * security_read_policy - read the policy. | |
3952 | * @data: binary policy data | |
3953 | * @len: length of data in bytes | |
3954 | * | |
3955 | */ | |
e67b7985 | 3956 | int security_read_policy(void **data, size_t *len) |
cee74f47 | 3957 | { |
e67b7985 | 3958 | struct selinux_state *state = &selinux_state; |
1b8b31a2 | 3959 | struct selinux_policy *policy; |
cee74f47 | 3960 | |
66ccd256 OM |
3961 | policy = rcu_dereference_protected( |
3962 | state->policy, lockdep_is_held(&state->policy_mutex)); | |
3963 | if (!policy) | |
cee74f47 EP |
3964 | return -EINVAL; |
3965 | ||
66ccd256 | 3966 | *len = policy->policydb.len; |
845ca30f | 3967 | *data = vmalloc_user(*len); |
cee74f47 EP |
3968 | if (!*data) |
3969 | return -ENOMEM; | |
3970 | ||
fdd1ffe8 LR |
3971 | return __security_read_policy(policy, *data, len); |
3972 | } | |
cee74f47 | 3973 | |
fdd1ffe8 LR |
3974 | /** |
3975 | * security_read_state_kernel - read the policy. | |
fdd1ffe8 LR |
3976 | * @data: binary policy data |
3977 | * @len: length of data in bytes | |
3978 | * | |
3979 | * Allocates kernel memory for reading SELinux policy. | |
3980 | * This function is for internal use only and should not | |
3981 | * be used for returning data to user space. | |
3982 | * | |
3983 | * This function must be called with policy_mutex held. | |
3984 | */ | |
e67b7985 | 3985 | int security_read_state_kernel(void **data, size_t *len) |
fdd1ffe8 | 3986 | { |
73de1bef | 3987 | int err; |
e67b7985 | 3988 | struct selinux_state *state = &selinux_state; |
fdd1ffe8 | 3989 | struct selinux_policy *policy; |
cee74f47 | 3990 | |
fdd1ffe8 LR |
3991 | policy = rcu_dereference_protected( |
3992 | state->policy, lockdep_is_held(&state->policy_mutex)); | |
3993 | if (!policy) | |
3994 | return -EINVAL; | |
3995 | ||
3996 | *len = policy->policydb.len; | |
3997 | *data = vmalloc(*len); | |
3998 | if (!*data) | |
3999 | return -ENOMEM; | |
cee74f47 | 4000 | |
73de1bef XJ |
4001 | err = __security_read_policy(policy, *data, len); |
4002 | if (err) { | |
4003 | vfree(*data); | |
4004 | *data = NULL; | |
4005 | *len = 0; | |
4006 | } | |
4007 | return err; | |
cee74f47 | 4008 | } |