/src/selinux/libsepol/src/services.c
Line | Count | Source |
1 | | /* |
2 | | * Author : Stephen Smalley, <stephen.smalley.work@gmail.com> |
3 | | */ |
4 | | /* |
5 | | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> |
6 | | * |
7 | | * Support for enhanced MLS infrastructure. |
8 | | * |
9 | | * Updated: Frank Mayer <mayerf@tresys.com> |
10 | | * and Karl MacMillan <kmacmillan@tresys.com> |
11 | | * |
12 | | * Added conditional policy language extensions |
13 | | * |
14 | | * Updated: Red Hat, Inc. James Morris <jmorris@redhat.com> |
15 | | * |
16 | | * Fine-grained netlink support |
17 | | * IPv6 support |
18 | | * Code cleanup |
19 | | * |
20 | | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. |
21 | | * Copyright (C) 2003 - 2004 Tresys Technology, LLC |
22 | | * Copyright (C) 2003 - 2004 Red Hat, Inc. |
23 | | * Copyright (C) 2017 Mellanox Technologies Inc. |
24 | | * |
25 | | * This library is free software; you can redistribute it and/or |
26 | | * modify it under the terms of the GNU Lesser General Public |
27 | | * License as published by the Free Software Foundation; either |
28 | | * version 2.1 of the License, or (at your option) any later version. |
29 | | * |
30 | | * This library is distributed in the hope that it will be useful, |
31 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
32 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
33 | | * Lesser General Public License for more details. |
34 | | * |
35 | | * You should have received a copy of the GNU Lesser General Public |
36 | | * License along with this library; if not, write to the Free Software |
37 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
38 | | */ |
39 | | |
40 | | /* FLASK */ |
41 | | |
42 | | /* |
43 | | * Implementation of the security services. |
44 | | */ |
45 | | |
46 | | /* Initial sizes malloc'd for sepol_compute_av_reason_buffer() support */ |
47 | 0 | #define REASON_BUF_SIZE 2048 |
48 | 0 | #define EXPR_BUF_SIZE 1024 |
49 | 0 | #define STACK_LEN 32 |
50 | | |
51 | | #include <stdlib.h> |
52 | | #include <sys/types.h> |
53 | | #include <sys/socket.h> |
54 | | #include <netinet/in.h> |
55 | | #include <arpa/inet.h> |
56 | | |
57 | | #include <sepol/policydb/policydb.h> |
58 | | #include <sepol/policydb/sidtab.h> |
59 | | #include <sepol/policydb/services.h> |
60 | | #include <sepol/policydb/conditional.h> |
61 | | #include <sepol/policydb/util.h> |
62 | | #include <sepol/sepol.h> |
63 | | |
64 | | #include "debug.h" |
65 | | #include "private.h" |
66 | | #include "context.h" |
67 | | #include "mls.h" |
68 | | #include "flask.h" |
69 | | |
70 | | #define BUG() \ |
71 | 0 | do { \ |
72 | 0 | ERR(NULL, "Badness at %s:%d", __FILE__, __LINE__); \ |
73 | 0 | } while (0) |
74 | | |
75 | | static int selinux_enforcing = 1; |
76 | | |
77 | | static sidtab_t mysidtab, *sidtab = &mysidtab; |
78 | | static policydb_t mypolicydb, *policydb = &mypolicydb; |
79 | | |
80 | | /* Used by sepol_compute_av_reason_buffer() to keep track of entries */ |
81 | | static int reason_buf_used; |
82 | | static int reason_buf_len; |
83 | | |
84 | | /* Stack services for RPN to infix conversion. */ |
85 | | static char **stack; |
86 | | static int stack_len; |
87 | | static int next_stack_entry; |
88 | | |
89 | | static void push(char *expr_ptr) |
90 | 0 | { |
91 | 0 | if (next_stack_entry >= stack_len) { |
92 | 0 | char **new_stack; |
93 | 0 | int new_stack_len; |
94 | |
|
95 | 0 | if (stack_len == 0) |
96 | 0 | new_stack_len = STACK_LEN; |
97 | 0 | else |
98 | 0 | new_stack_len = stack_len * 2; |
99 | |
|
100 | 0 | new_stack = reallocarray(stack, new_stack_len, sizeof(*stack)); |
101 | 0 | if (!new_stack) { |
102 | 0 | ERR(NULL, "unable to allocate stack space"); |
103 | 0 | return; |
104 | 0 | } |
105 | 0 | stack_len = new_stack_len; |
106 | 0 | stack = new_stack; |
107 | 0 | } |
108 | 0 | stack[next_stack_entry] = expr_ptr; |
109 | 0 | next_stack_entry++; |
110 | 0 | } |
111 | | |
112 | | static char *pop(void) |
113 | 0 | { |
114 | 0 | next_stack_entry--; |
115 | 0 | if (next_stack_entry < 0) { |
116 | 0 | next_stack_entry = 0; |
117 | 0 | ERR(NULL, "pop called with no stack entries"); |
118 | 0 | return NULL; |
119 | 0 | } |
120 | 0 | return stack[next_stack_entry]; |
121 | 0 | } |
122 | | /* End Stack services */ |
123 | | |
124 | | int sepol_set_sidtab(sidtab_t *s) |
125 | 0 | { |
126 | 0 | sidtab = s; |
127 | 0 | return 0; |
128 | 0 | } |
129 | | |
130 | | int sepol_set_policydb(policydb_t *p) |
131 | 0 | { |
132 | 0 | policydb = p; |
133 | 0 | return 0; |
134 | 0 | } |
135 | | |
136 | | int sepol_set_policydb_from_file(FILE *fp) |
137 | 0 | { |
138 | 0 | struct policy_file pf; |
139 | |
|
140 | 0 | policy_file_init(&pf); |
141 | 0 | pf.fp = fp; |
142 | 0 | pf.type = PF_USE_STDIO; |
143 | 0 | if (mypolicydb.policy_type) |
144 | 0 | policydb_destroy(&mypolicydb); |
145 | 0 | if (policydb_init(&mypolicydb)) { |
146 | 0 | ERR(NULL, "Out of memory!"); |
147 | 0 | return -1; |
148 | 0 | } |
149 | 0 | if (policydb_read(&mypolicydb, &pf, 0)) { |
150 | 0 | policydb_destroy(&mypolicydb); |
151 | 0 | ERR(NULL, "can't read binary policy: %m"); |
152 | 0 | return -1; |
153 | 0 | } |
154 | 0 | policydb = &mypolicydb; |
155 | 0 | return sepol_sidtab_init(sidtab); |
156 | 0 | } |
157 | | |
158 | | /* |
159 | | * The largest sequence number that has been used when |
160 | | * providing an access decision to the access vector cache. |
161 | | * The sequence number only changes when a policy change |
162 | | * occurs. |
163 | | */ |
164 | | static uint32_t latest_granting = 0; |
165 | | |
166 | | /* |
167 | | * cat_expr_buf adds a string to an expression buffer and handles |
168 | | * realloc's if buffer is too small. The array of expression text |
169 | | * buffer pointers and its counter are globally defined here as |
170 | | * constraint_expr_eval_reason() sets them up and cat_expr_buf |
171 | | * updates the e_buf pointer. |
172 | | */ |
173 | | static int expr_counter; |
174 | | static char **expr_list; |
175 | | static int expr_buf_used; |
176 | | static int expr_buf_len; |
177 | | |
178 | | static void cat_expr_buf(char *e_buf, const char *string) |
179 | 0 | { |
180 | 0 | int len, new_buf_len; |
181 | 0 | char *p, *new_buf; |
182 | |
|
183 | 0 | while (1) { |
184 | 0 | p = e_buf + expr_buf_used; |
185 | 0 | len = snprintf(p, expr_buf_len - expr_buf_used, "%s", string); |
186 | 0 | if (len < 0 || len >= expr_buf_len - expr_buf_used) { |
187 | 0 | new_buf_len = expr_buf_len + EXPR_BUF_SIZE; |
188 | 0 | new_buf = realloc(e_buf, new_buf_len); |
189 | 0 | if (!new_buf) { |
190 | 0 | ERR(NULL, "failed to realloc expr buffer"); |
191 | 0 | return; |
192 | 0 | } |
193 | | /* Update new ptr in expr list and locally + new len */ |
194 | 0 | expr_list[expr_counter] = new_buf; |
195 | 0 | e_buf = new_buf; |
196 | 0 | expr_buf_len = new_buf_len; |
197 | 0 | } else { |
198 | 0 | expr_buf_used += len; |
199 | 0 | return; |
200 | 0 | } |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | /* |
205 | | * For 'types' only, read the types_names->types list as it will |
206 | | * contain a list of types and attributes that were defined in the |
207 | | * policy source. |
208 | | * For user and role just read the e->names list. |
209 | | */ |
210 | | static void get_name_list(constraint_expr_t *e, int type, const char *src, |
211 | | const char *op, int failed) |
212 | 0 | { |
213 | 0 | ebitmap_t *types; |
214 | 0 | int rc = 0; |
215 | 0 | unsigned int i; |
216 | 0 | char tmp_buf[128]; |
217 | 0 | int counter = 0; |
218 | |
|
219 | 0 | if (policydb->policy_type == POLICY_KERN && type == CEXPR_TYPE) |
220 | 0 | types = &e->type_names->types; |
221 | 0 | else |
222 | 0 | types = &e->names; |
223 | | |
224 | | /* Find out how many entries */ |
225 | 0 | for (i = ebitmap_startbit(types); i < ebitmap_length(types); i++) { |
226 | 0 | rc = ebitmap_get_bit(types, i); |
227 | 0 | if (rc == 0) |
228 | 0 | continue; |
229 | 0 | else |
230 | 0 | counter++; |
231 | 0 | } |
232 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "(%s%s", src, op); |
233 | 0 | cat_expr_buf(expr_list[expr_counter], tmp_buf); |
234 | |
|
235 | 0 | if (counter == 0) |
236 | 0 | cat_expr_buf(expr_list[expr_counter], "<empty_set> "); |
237 | 0 | if (counter > 1) |
238 | 0 | cat_expr_buf(expr_list[expr_counter], " {"); |
239 | 0 | if (counter >= 1) { |
240 | 0 | for (i = ebitmap_startbit(types); i < ebitmap_length(types); |
241 | 0 | i++) { |
242 | 0 | rc = ebitmap_get_bit(types, i); |
243 | 0 | if (rc == 0) |
244 | 0 | continue; |
245 | | |
246 | | /* Collect entries */ |
247 | 0 | switch (type) { |
248 | 0 | case CEXPR_USER: |
249 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), " %s", |
250 | 0 | policydb->p_user_val_to_name[i]); |
251 | 0 | break; |
252 | 0 | case CEXPR_ROLE: |
253 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), " %s", |
254 | 0 | policydb->p_role_val_to_name[i]); |
255 | 0 | break; |
256 | 0 | case CEXPR_TYPE: |
257 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), " %s", |
258 | 0 | policydb->p_type_val_to_name[i]); |
259 | 0 | break; |
260 | 0 | } |
261 | 0 | cat_expr_buf(expr_list[expr_counter], tmp_buf); |
262 | 0 | } |
263 | 0 | } |
264 | 0 | if (counter > 1) |
265 | 0 | cat_expr_buf(expr_list[expr_counter], " }"); |
266 | 0 | if (failed) |
267 | 0 | cat_expr_buf(expr_list[expr_counter], " -Fail-) "); |
268 | 0 | else |
269 | 0 | cat_expr_buf(expr_list[expr_counter], ") "); |
270 | |
|
271 | 0 | return; |
272 | 0 | } |
273 | | |
274 | | static void msgcat(const char *src, const char *tgt, const char *op, int failed) |
275 | 0 | { |
276 | 0 | char tmp_buf[128]; |
277 | 0 | if (failed) |
278 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s -Fail-) ", src, |
279 | 0 | op, tgt); |
280 | 0 | else |
281 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "(%s %s %s) ", src, op, tgt); |
282 | 0 | cat_expr_buf(expr_list[expr_counter], tmp_buf); |
283 | 0 | } |
284 | | |
285 | | /* Returns a buffer with class, statement type and permissions */ |
286 | | static char *get_class_info(sepol_security_class_t tclass, |
287 | | constraint_node_t *constraint, |
288 | | context_struct_t *xcontext) |
289 | 0 | { |
290 | 0 | constraint_expr_t *e; |
291 | 0 | int mls, state_num; |
292 | | /* Determine statement type */ |
293 | 0 | const char *statements[] = { "constrain ", /* 0 */ |
294 | 0 | "mlsconstrain ", /* 1 */ |
295 | 0 | "validatetrans ", /* 2 */ |
296 | 0 | "mlsvalidatetrans ", /* 3 */ |
297 | 0 | NULL }; |
298 | 0 | size_t class_buf_len = 0; |
299 | 0 | size_t new_class_buf_len; |
300 | 0 | size_t buf_used; |
301 | 0 | int len; |
302 | 0 | char *class_buf = NULL, *p; |
303 | 0 | char *new_class_buf = NULL; |
304 | | |
305 | | /* Find if MLS statement or not */ |
306 | 0 | mls = 0; |
307 | 0 | for (e = constraint->expr; e; e = e->next) { |
308 | 0 | if (e->attr >= CEXPR_L1L2) { |
309 | 0 | mls = 1; |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | } |
313 | |
|
314 | 0 | if (xcontext == NULL) |
315 | 0 | state_num = mls + 0; |
316 | 0 | else |
317 | 0 | state_num = mls + 2; |
318 | |
|
319 | 0 | while (1) { |
320 | 0 | new_class_buf_len = class_buf_len + EXPR_BUF_SIZE; |
321 | 0 | new_class_buf = realloc(class_buf, new_class_buf_len); |
322 | 0 | if (!new_class_buf) { |
323 | 0 | free(class_buf); |
324 | 0 | return NULL; |
325 | 0 | } |
326 | 0 | class_buf_len = new_class_buf_len; |
327 | 0 | class_buf = new_class_buf; |
328 | 0 | buf_used = 0; |
329 | 0 | p = class_buf; |
330 | | |
331 | | /* Add statement type */ |
332 | 0 | len = snprintf(p, class_buf_len - buf_used, "%s", |
333 | 0 | statements[state_num]); |
334 | 0 | if (len < 0 || (size_t)len >= class_buf_len - buf_used) |
335 | 0 | continue; |
336 | | |
337 | | /* Add class entry */ |
338 | 0 | p += len; |
339 | 0 | buf_used += len; |
340 | 0 | len = snprintf(p, class_buf_len - buf_used, "%s ", |
341 | 0 | policydb->p_class_val_to_name[tclass - 1]); |
342 | 0 | if (len < 0 || (size_t)len >= class_buf_len - buf_used) |
343 | 0 | continue; |
344 | | |
345 | | /* Add permission entries (validatetrans does not have perms) */ |
346 | 0 | p += len; |
347 | 0 | buf_used += len; |
348 | 0 | if (state_num < 2) { |
349 | 0 | char *permstr = sepol_av_to_string( |
350 | 0 | policydb, tclass, constraint->permissions); |
351 | |
|
352 | 0 | len = snprintf(p, class_buf_len - buf_used, "{%s } (", |
353 | 0 | permstr ?: "<format-failure>"); |
354 | 0 | free(permstr); |
355 | 0 | } else { |
356 | 0 | len = snprintf(p, class_buf_len - buf_used, "("); |
357 | 0 | } |
358 | 0 | if (len < 0 || (size_t)len >= class_buf_len - buf_used) |
359 | 0 | continue; |
360 | 0 | break; |
361 | 0 | } |
362 | 0 | return class_buf; |
363 | 0 | } |
364 | | |
365 | | /* |
366 | | * Modified version of constraint_expr_eval that will process each |
367 | | * constraint as before but adds the information to text buffers that |
368 | | * will hold various components. The expression will be in RPN format, |
369 | | * therefore there is a stack based RPN to infix converter to produce |
370 | | * the final readable constraint. |
371 | | * |
372 | | * Return the boolean value of a constraint expression |
373 | | * when it is applied to the specified source and target |
374 | | * security contexts. |
375 | | * |
376 | | * xcontext is a special beast... It is used by the validatetrans rules |
377 | | * only. For these rules, scontext is the context before the transition, |
378 | | * tcontext is the context after the transition, and xcontext is the |
379 | | * context of the process performing the transition. All other callers |
380 | | * of constraint_expr_eval_reason should pass in NULL for xcontext. |
381 | | * |
382 | | * This function will also build a buffer as the constraint is processed |
383 | | * for analysis. If this option is not required, then: |
384 | | * 'tclass' should be '0' and r_buf MUST be NULL. |
385 | | */ |
386 | | static int constraint_expr_eval_reason(context_struct_t *scontext, |
387 | | context_struct_t *tcontext, |
388 | | context_struct_t *xcontext, |
389 | | sepol_security_class_t tclass, |
390 | | constraint_node_t *constraint, |
391 | | char **r_buf, unsigned int flags) |
392 | 0 | { |
393 | 0 | uint32_t val1, val2; |
394 | 0 | context_struct_t *c; |
395 | 0 | role_datum_t *r1, *r2; |
396 | 0 | mls_level_t *l1, *l2; |
397 | 0 | constraint_expr_t *e; |
398 | 0 | int s[CEXPR_MAXDEPTH] = {}; |
399 | 0 | int sp = -1; |
400 | 0 | char tmp_buf[128]; |
401 | | |
402 | | /* |
403 | | * Define the s_t_x_num values that make up r1, t2 etc. in text strings |
404 | | * Set 1 = source, 2 = target, 3 = xcontext for validatetrans |
405 | | */ |
406 | 0 | #define SOURCE 1 |
407 | 0 | #define TARGET 2 |
408 | 0 | #define XTARGET 3 |
409 | |
|
410 | 0 | int s_t_x_num; |
411 | | |
412 | | /* Set 0 = fail, u = CEXPR_USER, r = CEXPR_ROLE, t = CEXPR_TYPE */ |
413 | 0 | int u_r_t = 0; |
414 | |
|
415 | 0 | char *src = NULL; |
416 | 0 | char *tgt = NULL; |
417 | 0 | int rc = 0, x; |
418 | 0 | char *class_buf = NULL; |
419 | 0 | int expr_list_len = 0; |
420 | 0 | int expr_count; |
421 | | |
422 | | /* |
423 | | * The array of expression answer buffer pointers and counter. |
424 | | */ |
425 | 0 | char **answer_list = NULL; |
426 | 0 | int answer_counter = 0; |
427 | | |
428 | | /* The pop operands */ |
429 | 0 | char *a; |
430 | 0 | char *b; |
431 | 0 | int a_len, b_len; |
432 | |
|
433 | 0 | class_buf = get_class_info(tclass, constraint, xcontext); |
434 | 0 | if (!class_buf) { |
435 | 0 | ERR(NULL, "failed to allocate class buffer"); |
436 | 0 | return -ENOMEM; |
437 | 0 | } |
438 | | |
439 | | /* Original function but with buffer support */ |
440 | 0 | expr_counter = 0; |
441 | 0 | expr_list = NULL; |
442 | 0 | for (e = constraint->expr; e; e = e->next) { |
443 | | /* Allocate a stack to hold expression buffer entries */ |
444 | 0 | if (expr_counter >= expr_list_len) { |
445 | 0 | char **new_expr_list; |
446 | 0 | int new_expr_list_len; |
447 | |
|
448 | 0 | if (expr_list_len == 0) |
449 | 0 | new_expr_list_len = STACK_LEN; |
450 | 0 | else |
451 | 0 | new_expr_list_len = expr_list_len * 2; |
452 | |
|
453 | 0 | new_expr_list = reallocarray(expr_list, |
454 | 0 | new_expr_list_len, |
455 | 0 | sizeof(*expr_list)); |
456 | 0 | if (!new_expr_list) { |
457 | 0 | ERR(NULL, |
458 | 0 | "failed to allocate expr buffer stack"); |
459 | 0 | rc = -ENOMEM; |
460 | 0 | goto out; |
461 | 0 | } |
462 | 0 | expr_list_len = new_expr_list_len; |
463 | 0 | expr_list = new_expr_list; |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * malloc a buffer to store each expression text component. If |
468 | | * buffer is too small cat_expr_buf() will realloc extra space. |
469 | | */ |
470 | 0 | expr_buf_len = EXPR_BUF_SIZE; |
471 | 0 | expr_list[expr_counter] = malloc(expr_buf_len); |
472 | 0 | if (!expr_list[expr_counter]) { |
473 | 0 | ERR(NULL, "failed to allocate expr buffer"); |
474 | 0 | rc = -ENOMEM; |
475 | 0 | goto out; |
476 | 0 | } |
477 | 0 | expr_buf_used = 0; |
478 | | |
479 | | /* Now process each expression of the constraint */ |
480 | 0 | switch (e->expr_type) { |
481 | 0 | case CEXPR_NOT: |
482 | 0 | if (sp < 0) { |
483 | 0 | BUG(); |
484 | 0 | rc = -EINVAL; |
485 | 0 | goto out; |
486 | 0 | } |
487 | 0 | s[sp] = !s[sp]; |
488 | 0 | cat_expr_buf(expr_list[expr_counter], "not"); |
489 | 0 | break; |
490 | 0 | case CEXPR_AND: |
491 | 0 | if (sp < 1) { |
492 | 0 | BUG(); |
493 | 0 | rc = -EINVAL; |
494 | 0 | goto out; |
495 | 0 | } |
496 | 0 | sp--; |
497 | 0 | s[sp] &= s[sp + 1]; |
498 | 0 | cat_expr_buf(expr_list[expr_counter], "and"); |
499 | 0 | break; |
500 | 0 | case CEXPR_OR: |
501 | 0 | if (sp < 1) { |
502 | 0 | BUG(); |
503 | 0 | rc = -EINVAL; |
504 | 0 | goto out; |
505 | 0 | } |
506 | 0 | sp--; |
507 | 0 | s[sp] |= s[sp + 1]; |
508 | 0 | cat_expr_buf(expr_list[expr_counter], "or"); |
509 | 0 | break; |
510 | 0 | case CEXPR_ATTR: |
511 | 0 | if (sp == (CEXPR_MAXDEPTH - 1)) |
512 | 0 | goto out; |
513 | | |
514 | 0 | switch (e->attr) { |
515 | 0 | case CEXPR_USER: |
516 | 0 | val1 = scontext->user; |
517 | 0 | val2 = tcontext->user; |
518 | 0 | free(src); |
519 | 0 | src = strdup("u1"); |
520 | 0 | free(tgt); |
521 | 0 | tgt = strdup("u2"); |
522 | 0 | break; |
523 | 0 | case CEXPR_TYPE: |
524 | 0 | val1 = scontext->type; |
525 | 0 | val2 = tcontext->type; |
526 | 0 | free(src); |
527 | 0 | src = strdup("t1"); |
528 | 0 | free(tgt); |
529 | 0 | tgt = strdup("t2"); |
530 | 0 | break; |
531 | 0 | case CEXPR_ROLE: |
532 | 0 | val1 = scontext->role; |
533 | 0 | val2 = tcontext->role; |
534 | 0 | r1 = policydb->role_val_to_struct[val1 - 1]; |
535 | 0 | r2 = policydb->role_val_to_struct[val2 - 1]; |
536 | 0 | free(src); |
537 | 0 | src = strdup("r1"); |
538 | 0 | free(tgt); |
539 | 0 | tgt = strdup("r2"); |
540 | |
|
541 | 0 | switch (e->op) { |
542 | 0 | case CEXPR_DOM: |
543 | 0 | s[++sp] = ebitmap_get_bit( |
544 | 0 | &r1->dominates, val2 - 1); |
545 | 0 | msgcat(src, tgt, "dom", s[sp] == 0); |
546 | 0 | expr_counter++; |
547 | 0 | continue; |
548 | 0 | case CEXPR_DOMBY: |
549 | 0 | s[++sp] = ebitmap_get_bit( |
550 | 0 | &r2->dominates, val1 - 1); |
551 | 0 | msgcat(src, tgt, "domby", s[sp] == 0); |
552 | 0 | expr_counter++; |
553 | 0 | continue; |
554 | 0 | case CEXPR_INCOMP: |
555 | 0 | s[++sp] = (!ebitmap_get_bit( |
556 | 0 | &r1->dominates, |
557 | 0 | val2 - 1) && |
558 | 0 | !ebitmap_get_bit( |
559 | 0 | &r2->dominates, |
560 | 0 | val1 - 1)); |
561 | 0 | msgcat(src, tgt, "incomp", s[sp] == 0); |
562 | 0 | expr_counter++; |
563 | 0 | continue; |
564 | 0 | default: |
565 | 0 | break; |
566 | 0 | } |
567 | 0 | break; |
568 | 0 | case CEXPR_L1L2: |
569 | 0 | l1 = &(scontext->range.level[0]); |
570 | 0 | l2 = &(tcontext->range.level[0]); |
571 | 0 | free(src); |
572 | 0 | src = strdup("l1"); |
573 | 0 | free(tgt); |
574 | 0 | tgt = strdup("l2"); |
575 | 0 | goto mls_ops; |
576 | 0 | case CEXPR_L1H2: |
577 | 0 | l1 = &(scontext->range.level[0]); |
578 | 0 | l2 = &(tcontext->range.level[1]); |
579 | 0 | free(src); |
580 | 0 | src = strdup("l1"); |
581 | 0 | free(tgt); |
582 | 0 | tgt = strdup("h2"); |
583 | 0 | goto mls_ops; |
584 | 0 | case CEXPR_H1L2: |
585 | 0 | l1 = &(scontext->range.level[1]); |
586 | 0 | l2 = &(tcontext->range.level[0]); |
587 | 0 | free(src); |
588 | 0 | src = strdup("h1"); |
589 | 0 | free(tgt); |
590 | 0 | tgt = strdup("l2"); |
591 | 0 | goto mls_ops; |
592 | 0 | case CEXPR_H1H2: |
593 | 0 | l1 = &(scontext->range.level[1]); |
594 | 0 | l2 = &(tcontext->range.level[1]); |
595 | 0 | free(src); |
596 | 0 | src = strdup("h1"); |
597 | 0 | free(tgt); |
598 | 0 | tgt = strdup("h2"); |
599 | 0 | goto mls_ops; |
600 | 0 | case CEXPR_L1H1: |
601 | 0 | l1 = &(scontext->range.level[0]); |
602 | 0 | l2 = &(scontext->range.level[1]); |
603 | 0 | free(src); |
604 | 0 | src = strdup("l1"); |
605 | 0 | free(tgt); |
606 | 0 | tgt = strdup("h1"); |
607 | 0 | goto mls_ops; |
608 | 0 | case CEXPR_L2H2: |
609 | 0 | l1 = &(tcontext->range.level[0]); |
610 | 0 | l2 = &(tcontext->range.level[1]); |
611 | 0 | free(src); |
612 | 0 | src = strdup("l2"); |
613 | 0 | free(tgt); |
614 | 0 | tgt = strdup("h2"); |
615 | 0 | mls_ops: |
616 | 0 | switch (e->op) { |
617 | 0 | case CEXPR_EQ: |
618 | 0 | s[++sp] = mls_level_eq(l1, l2); |
619 | 0 | msgcat(src, tgt, "eq", s[sp] == 0); |
620 | 0 | expr_counter++; |
621 | 0 | continue; |
622 | 0 | case CEXPR_NEQ: |
623 | 0 | s[++sp] = !mls_level_eq(l1, l2); |
624 | 0 | msgcat(src, tgt, "!=", s[sp] == 0); |
625 | 0 | expr_counter++; |
626 | 0 | continue; |
627 | 0 | case CEXPR_DOM: |
628 | 0 | s[++sp] = mls_level_dom(l1, l2); |
629 | 0 | msgcat(src, tgt, "dom", s[sp] == 0); |
630 | 0 | expr_counter++; |
631 | 0 | continue; |
632 | 0 | case CEXPR_DOMBY: |
633 | 0 | s[++sp] = mls_level_dom(l2, l1); |
634 | 0 | msgcat(src, tgt, "domby", s[sp] == 0); |
635 | 0 | expr_counter++; |
636 | 0 | continue; |
637 | 0 | case CEXPR_INCOMP: |
638 | 0 | s[++sp] = mls_level_incomp(l2, l1); |
639 | 0 | msgcat(src, tgt, "incomp", s[sp] == 0); |
640 | 0 | expr_counter++; |
641 | 0 | continue; |
642 | 0 | default: |
643 | 0 | BUG(); |
644 | 0 | goto out; |
645 | 0 | } |
646 | 0 | break; |
647 | 0 | default: |
648 | 0 | BUG(); |
649 | 0 | goto out; |
650 | 0 | } |
651 | | |
652 | 0 | switch (e->op) { |
653 | 0 | case CEXPR_EQ: |
654 | 0 | s[++sp] = (val1 == val2); |
655 | 0 | msgcat(src, tgt, "==", s[sp] == 0); |
656 | 0 | break; |
657 | 0 | case CEXPR_NEQ: |
658 | 0 | s[++sp] = (val1 != val2); |
659 | 0 | msgcat(src, tgt, "!=", s[sp] == 0); |
660 | 0 | break; |
661 | 0 | default: |
662 | 0 | BUG(); |
663 | 0 | goto out; |
664 | 0 | } |
665 | 0 | break; |
666 | 0 | case CEXPR_NAMES: |
667 | 0 | if (sp == (CEXPR_MAXDEPTH - 1)) |
668 | 0 | goto out; |
669 | 0 | s_t_x_num = SOURCE; |
670 | 0 | c = scontext; |
671 | 0 | if (e->attr & CEXPR_TARGET) { |
672 | 0 | s_t_x_num = TARGET; |
673 | 0 | c = tcontext; |
674 | 0 | } else if (e->attr & CEXPR_XTARGET) { |
675 | 0 | s_t_x_num = XTARGET; |
676 | 0 | c = xcontext; |
677 | 0 | } |
678 | 0 | if (!c) { |
679 | 0 | BUG(); |
680 | 0 | goto out; |
681 | 0 | } |
682 | 0 | if (e->attr & CEXPR_USER) { |
683 | 0 | u_r_t = CEXPR_USER; |
684 | 0 | val1 = c->user; |
685 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "u%d ", |
686 | 0 | s_t_x_num); |
687 | 0 | free(src); |
688 | 0 | src = strdup(tmp_buf); |
689 | 0 | } else if (e->attr & CEXPR_ROLE) { |
690 | 0 | u_r_t = CEXPR_ROLE; |
691 | 0 | val1 = c->role; |
692 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "r%d ", |
693 | 0 | s_t_x_num); |
694 | 0 | free(src); |
695 | 0 | src = strdup(tmp_buf); |
696 | 0 | } else if (e->attr & CEXPR_TYPE) { |
697 | 0 | u_r_t = CEXPR_TYPE; |
698 | 0 | val1 = c->type; |
699 | 0 | snprintf(tmp_buf, sizeof(tmp_buf), "t%d ", |
700 | 0 | s_t_x_num); |
701 | 0 | free(src); |
702 | 0 | src = strdup(tmp_buf); |
703 | 0 | } else { |
704 | 0 | BUG(); |
705 | 0 | goto out; |
706 | 0 | } |
707 | | |
708 | 0 | switch (e->op) { |
709 | 0 | case CEXPR_EQ: |
710 | 0 | s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); |
711 | 0 | get_name_list(e, u_r_t, src, "==", s[sp] == 0); |
712 | 0 | break; |
713 | | |
714 | 0 | case CEXPR_NEQ: |
715 | 0 | s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); |
716 | 0 | get_name_list(e, u_r_t, src, "!=", s[sp] == 0); |
717 | 0 | break; |
718 | 0 | default: |
719 | 0 | BUG(); |
720 | 0 | goto out; |
721 | 0 | } |
722 | 0 | break; |
723 | 0 | default: |
724 | 0 | BUG(); |
725 | 0 | goto out; |
726 | 0 | } |
727 | 0 | expr_counter++; |
728 | 0 | } |
729 | | |
730 | | /* |
731 | | * At this point each expression of the constraint is in |
732 | | * expr_list[n+1] and in RPN format. Now convert to 'infix' |
733 | | */ |
734 | | |
735 | | /* |
736 | | * Save expr count but zero expr_counter to detect if |
737 | | * 'BUG(); goto out;' was called as we need to release any used |
738 | | * expr_list malloc's. Normally they are released by the RPN to |
739 | | * infix code. |
740 | | */ |
741 | 0 | expr_count = expr_counter; |
742 | 0 | expr_counter = 0; |
743 | | |
744 | | /* |
745 | | * Generate the same number of answer buffer entries as expression |
746 | | * buffers (as there will never be more). |
747 | | */ |
748 | 0 | answer_list = calloc(expr_count, sizeof(*answer_list)); |
749 | 0 | if (!answer_list) { |
750 | 0 | ERR(NULL, "failed to allocate answer stack"); |
751 | 0 | rc = -ENOMEM; |
752 | 0 | goto out; |
753 | 0 | } |
754 | | |
755 | | /* Convert constraint from RPN to infix notation. */ |
756 | 0 | for (x = 0; x != expr_count; x++) { |
757 | 0 | if (strncmp(expr_list[x], "and", 3) == 0 || |
758 | 0 | strncmp(expr_list[x], "or", 2) == 0) { |
759 | 0 | b = pop(); |
760 | 0 | b_len = strlen(b); |
761 | 0 | a = pop(); |
762 | 0 | a_len = strlen(a); |
763 | | |
764 | | /* get a buffer to hold the answer */ |
765 | 0 | answer_list[answer_counter] = malloc(a_len + b_len + 8); |
766 | 0 | if (!answer_list[answer_counter]) { |
767 | 0 | ERR(NULL, "failed to allocate answer buffer"); |
768 | 0 | rc = -ENOMEM; |
769 | 0 | goto out; |
770 | 0 | } |
771 | 0 | memset(answer_list[answer_counter], '\0', |
772 | 0 | a_len + b_len + 8); |
773 | |
|
774 | 0 | sprintf(answer_list[answer_counter], "%s %s %s", a, |
775 | 0 | expr_list[x], b); |
776 | 0 | push(answer_list[answer_counter++]); |
777 | 0 | free(a); |
778 | 0 | free(b); |
779 | 0 | free(expr_list[x]); |
780 | 0 | } else if (strncmp(expr_list[x], "not", 3) == 0) { |
781 | 0 | b = pop(); |
782 | 0 | b_len = strlen(b); |
783 | |
|
784 | 0 | answer_list[answer_counter] = malloc(b_len + 8); |
785 | 0 | if (!answer_list[answer_counter]) { |
786 | 0 | ERR(NULL, "failed to allocate answer buffer"); |
787 | 0 | rc = -ENOMEM; |
788 | 0 | goto out; |
789 | 0 | } |
790 | 0 | memset(answer_list[answer_counter], '\0', b_len + 8); |
791 | |
|
792 | 0 | if (strncmp(b, "not", 3) == 0) |
793 | 0 | sprintf(answer_list[answer_counter], "%s (%s)", |
794 | 0 | expr_list[x], b); |
795 | 0 | else |
796 | 0 | sprintf(answer_list[answer_counter], "%s%s", |
797 | 0 | expr_list[x], b); |
798 | 0 | push(answer_list[answer_counter++]); |
799 | 0 | free(b); |
800 | 0 | free(expr_list[x]); |
801 | 0 | } else { |
802 | 0 | push(expr_list[x]); |
803 | 0 | } |
804 | 0 | } |
805 | | /* Get the final answer from tos and build constraint text */ |
806 | 0 | a = pop(); |
807 | | |
808 | | /* validatetrans / constraint calculation: |
809 | | rc = 0 is denied, rc = 1 is granted */ |
810 | 0 | sprintf(tmp_buf, "%s %s\n", xcontext ? "Validatetrans" : "Constraint", |
811 | 0 | s[0] ? "GRANTED" : "DENIED"); |
812 | | |
813 | | /* |
814 | | * This will add the constraints to the callers reason buffer (who is |
815 | | * responsible for freeing the memory). It will handle any realloc's |
816 | | * should the buffer be too short. |
817 | | * The reason_buf_used and reason_buf_len counters are defined |
818 | | * globally as multiple constraints can be in the buffer. |
819 | | */ |
820 | |
|
821 | 0 | if (r_buf && ((s[0] == 0) || ((s[0] == 1 && (flags & SHOW_GRANTED) == |
822 | 0 | SHOW_GRANTED)))) { |
823 | 0 | int len; |
824 | 0 | char *p; |
825 | | /* |
826 | | * These contain the constraint components that are added to the |
827 | | * callers reason buffer. |
828 | | */ |
829 | 0 | const char *buffers[] = { class_buf, a, "); ", tmp_buf, NULL }; |
830 | |
|
831 | 0 | for (x = 0; buffers[x] != NULL; x++) { |
832 | 0 | while (1) { |
833 | 0 | p = *r_buf ? (*r_buf + reason_buf_used) : NULL; |
834 | 0 | len = snprintf(p, |
835 | 0 | reason_buf_len - reason_buf_used, |
836 | 0 | "%s", buffers[x]); |
837 | 0 | if (len < 0 || |
838 | 0 | len >= reason_buf_len - reason_buf_used) { |
839 | 0 | int new_buf_len = reason_buf_len + |
840 | 0 | REASON_BUF_SIZE; |
841 | 0 | char *new_buf = |
842 | 0 | realloc(*r_buf, new_buf_len); |
843 | 0 | if (!new_buf) { |
844 | 0 | ERR(NULL, |
845 | 0 | "failed to realloc reason buffer"); |
846 | 0 | goto out1; |
847 | 0 | } |
848 | 0 | *r_buf = new_buf; |
849 | 0 | reason_buf_len = new_buf_len; |
850 | 0 | continue; |
851 | 0 | } else { |
852 | 0 | reason_buf_used += len; |
853 | 0 | break; |
854 | 0 | } |
855 | 0 | } |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | 0 | out1: |
860 | 0 | rc = s[0]; |
861 | 0 | free(a); |
862 | |
|
863 | 0 | out: |
864 | 0 | free(class_buf); |
865 | 0 | free(src); |
866 | 0 | free(tgt); |
867 | |
|
868 | 0 | if (expr_counter) { |
869 | 0 | for (x = 0; expr_list[x] != NULL; x++) |
870 | 0 | free(expr_list[x]); |
871 | 0 | } |
872 | 0 | free(answer_list); |
873 | 0 | free(expr_list); |
874 | 0 | return rc; |
875 | 0 | } |
876 | | |
877 | | /* Forward declaration */ |
878 | | static int context_struct_compute_av(context_struct_t *scontext, |
879 | | context_struct_t *tcontext, |
880 | | sepol_security_class_t tclass, |
881 | | sepol_access_vector_t requested, |
882 | | struct sepol_av_decision *avd, |
883 | | unsigned int *reason, char **r_buf, |
884 | | unsigned int flags); |
885 | | |
886 | | static void type_attribute_bounds_av(context_struct_t *scontext, |
887 | | context_struct_t *tcontext, |
888 | | sepol_security_class_t tclass, |
889 | | sepol_access_vector_t requested, |
890 | | struct sepol_av_decision *avd, |
891 | | unsigned int *reason) |
892 | 0 | { |
893 | 0 | context_struct_t lo_scontext; |
894 | 0 | context_struct_t lo_tcontext, *tcontextp = tcontext; |
895 | 0 | struct sepol_av_decision lo_avd; |
896 | 0 | type_datum_t *source; |
897 | 0 | type_datum_t *target; |
898 | 0 | sepol_access_vector_t masked = 0; |
899 | |
|
900 | 0 | source = policydb->type_val_to_struct[scontext->type - 1]; |
901 | 0 | if (!source->bounds) |
902 | 0 | return; |
903 | | |
904 | 0 | target = policydb->type_val_to_struct[tcontext->type - 1]; |
905 | |
|
906 | 0 | memset(&lo_avd, 0, sizeof(lo_avd)); |
907 | |
|
908 | 0 | memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); |
909 | 0 | lo_scontext.type = source->bounds; |
910 | |
|
911 | 0 | if (target->bounds) { |
912 | 0 | memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); |
913 | 0 | lo_tcontext.type = target->bounds; |
914 | 0 | tcontextp = &lo_tcontext; |
915 | 0 | } |
916 | |
|
917 | 0 | context_struct_compute_av(&lo_scontext, tcontextp, tclass, requested, |
918 | 0 | &lo_avd, |
919 | 0 | NULL, /* reason intentionally omitted */ |
920 | 0 | NULL, 0); |
921 | |
|
922 | 0 | masked = ~lo_avd.allowed & avd->allowed; |
923 | |
|
924 | 0 | if (!masked) |
925 | 0 | return; /* no masked permission */ |
926 | | |
927 | | /* mask violated permissions */ |
928 | 0 | avd->allowed &= ~masked; |
929 | |
|
930 | 0 | if (reason) |
931 | 0 | *reason |= SEPOL_COMPUTEAV_BOUNDS; |
932 | 0 | } |
933 | | |
934 | | /* |
935 | | * Compute access vectors based on a context structure pair for |
936 | | * the permissions in a particular class. |
937 | | */ |
938 | | static int context_struct_compute_av(context_struct_t *scontext, |
939 | | context_struct_t *tcontext, |
940 | | sepol_security_class_t tclass, |
941 | | sepol_access_vector_t requested, |
942 | | struct sepol_av_decision *avd, |
943 | | unsigned int *reason, char **r_buf, |
944 | | unsigned int flags) |
945 | 0 | { |
946 | 0 | constraint_node_t *constraint; |
947 | 0 | struct role_allow *ra; |
948 | 0 | avtab_key_t avkey; |
949 | 0 | class_datum_t *tclass_datum; |
950 | 0 | avtab_ptr_t node; |
951 | 0 | ebitmap_t *sattr, *tattr; |
952 | 0 | ebitmap_node_t *snode, *tnode; |
953 | 0 | unsigned int i, j; |
954 | |
|
955 | 0 | if (!tclass || tclass > policydb->p_classes.nprim) { |
956 | 0 | ERR(NULL, "unrecognized class %d", tclass); |
957 | 0 | return -EINVAL; |
958 | 0 | } |
959 | 0 | tclass_datum = policydb->class_val_to_struct[tclass - 1]; |
960 | | |
961 | | /* |
962 | | * Initialize the access vectors to the default values. |
963 | | */ |
964 | 0 | avd->allowed = 0; |
965 | 0 | avd->decided = 0xffffffff; |
966 | 0 | avd->auditallow = 0; |
967 | 0 | avd->auditdeny = 0xffffffff; |
968 | 0 | avd->seqno = latest_granting; |
969 | 0 | if (reason) |
970 | 0 | *reason = 0; |
971 | | |
972 | | /* |
973 | | * If a specific type enforcement rule was defined for |
974 | | * this permission check, then use it. |
975 | | */ |
976 | 0 | avkey.target_class = tclass; |
977 | 0 | avkey.specified = AVTAB_AV; |
978 | 0 | sattr = &policydb->type_attr_map[scontext->type - 1]; |
979 | 0 | tattr = &policydb->type_attr_map[tcontext->type - 1]; |
980 | 0 | ebitmap_for_each_positive_bit(sattr, snode, i) { |
981 | 0 | ebitmap_for_each_positive_bit(tattr, tnode, j) { |
982 | 0 | avkey.source_type = i + 1; |
983 | 0 | avkey.target_type = j + 1; |
984 | 0 | for (node = avtab_search_node(&policydb->te_avtab, |
985 | 0 | &avkey); |
986 | 0 | node != NULL; node = avtab_search_node_next( |
987 | 0 | node, avkey.specified)) { |
988 | 0 | if (node->key.specified == AVTAB_ALLOWED) |
989 | 0 | avd->allowed |= node->datum.data; |
990 | 0 | else if (node->key.specified == |
991 | 0 | AVTAB_AUDITALLOW) |
992 | 0 | avd->auditallow |= node->datum.data; |
993 | 0 | else if (node->key.specified == AVTAB_AUDITDENY) |
994 | 0 | avd->auditdeny &= node->datum.data; |
995 | 0 | } |
996 | | |
997 | | /* Check conditional av table for additional permissions */ |
998 | 0 | cond_compute_av(&policydb->te_cond_avtab, &avkey, avd); |
999 | 0 | } |
1000 | 0 | } |
1001 | |
|
1002 | 0 | if (requested & ~avd->allowed) { |
1003 | 0 | if (reason) |
1004 | 0 | *reason |= SEPOL_COMPUTEAV_TE; |
1005 | 0 | requested &= avd->allowed; |
1006 | 0 | } |
1007 | | |
1008 | | /* |
1009 | | * Remove any permissions prohibited by a constraint (this includes |
1010 | | * the MLS policy). |
1011 | | */ |
1012 | 0 | constraint = tclass_datum->constraints; |
1013 | 0 | while (constraint) { |
1014 | 0 | if ((constraint->permissions & (avd->allowed)) && |
1015 | 0 | !constraint_expr_eval_reason(scontext, tcontext, NULL, |
1016 | 0 | tclass, constraint, r_buf, |
1017 | 0 | flags)) { |
1018 | 0 | avd->allowed = (avd->allowed) & |
1019 | 0 | ~(constraint->permissions); |
1020 | 0 | } |
1021 | 0 | constraint = constraint->next; |
1022 | 0 | } |
1023 | |
|
1024 | 0 | if (requested & ~avd->allowed) { |
1025 | 0 | if (reason) |
1026 | 0 | *reason |= SEPOL_COMPUTEAV_CONS; |
1027 | 0 | requested &= avd->allowed; |
1028 | 0 | } |
1029 | | |
1030 | | /* |
1031 | | * If checking process transition permission and the |
1032 | | * role is changing, then check the (current_role, new_role) |
1033 | | * pair. |
1034 | | */ |
1035 | 0 | if (tclass == policydb->process_class && |
1036 | 0 | (avd->allowed & policydb->process_trans_dyntrans) && |
1037 | 0 | scontext->role != tcontext->role) { |
1038 | 0 | for (ra = policydb->role_allow; ra; ra = ra->next) { |
1039 | 0 | if (scontext->role == ra->role && |
1040 | 0 | tcontext->role == ra->new_role) |
1041 | 0 | break; |
1042 | 0 | } |
1043 | 0 | if (!ra) |
1044 | 0 | avd->allowed &= ~policydb->process_trans_dyntrans; |
1045 | 0 | } |
1046 | |
|
1047 | 0 | if (requested & ~avd->allowed) { |
1048 | 0 | if (reason) |
1049 | 0 | *reason |= SEPOL_COMPUTEAV_RBAC; |
1050 | 0 | requested &= avd->allowed; |
1051 | 0 | } |
1052 | |
|
1053 | 0 | type_attribute_bounds_av(scontext, tcontext, tclass, requested, avd, |
1054 | 0 | reason); |
1055 | 0 | return 0; |
1056 | 0 | } |
1057 | | |
1058 | | /* |
1059 | | * sepol_validate_transition_reason_buffer - the reason buffer is realloc'd |
1060 | | * in the constraint_expr_eval_reason() function. |
1061 | | */ |
1062 | | int sepol_validate_transition_reason_buffer(sepol_security_id_t oldsid, |
1063 | | sepol_security_id_t newsid, |
1064 | | sepol_security_id_t tasksid, |
1065 | | sepol_security_class_t tclass, |
1066 | | char **reason_buf, |
1067 | | unsigned int flags) |
1068 | 0 | { |
1069 | 0 | context_struct_t *ocontext; |
1070 | 0 | context_struct_t *ncontext; |
1071 | 0 | context_struct_t *tcontext; |
1072 | 0 | class_datum_t *tclass_datum; |
1073 | 0 | constraint_node_t *constraint; |
1074 | |
|
1075 | 0 | if (!tclass || tclass > policydb->p_classes.nprim) { |
1076 | 0 | ERR(NULL, "unrecognized class %d", tclass); |
1077 | 0 | return -EINVAL; |
1078 | 0 | } |
1079 | 0 | tclass_datum = policydb->class_val_to_struct[tclass - 1]; |
1080 | |
|
1081 | 0 | ocontext = sepol_sidtab_search(sidtab, oldsid); |
1082 | 0 | if (!ocontext) { |
1083 | 0 | ERR(NULL, "unrecognized SID %d", oldsid); |
1084 | 0 | return -EINVAL; |
1085 | 0 | } |
1086 | | |
1087 | 0 | ncontext = sepol_sidtab_search(sidtab, newsid); |
1088 | 0 | if (!ncontext) { |
1089 | 0 | ERR(NULL, "unrecognized SID %d", newsid); |
1090 | 0 | return -EINVAL; |
1091 | 0 | } |
1092 | | |
1093 | 0 | tcontext = sepol_sidtab_search(sidtab, tasksid); |
1094 | 0 | if (!tcontext) { |
1095 | 0 | ERR(NULL, "unrecognized SID %d", tasksid); |
1096 | 0 | return -EINVAL; |
1097 | 0 | } |
1098 | | |
1099 | | /* |
1100 | | * Set the buffer to NULL as mls/validatetrans may not be processed. |
1101 | | * If a buffer is required, then the routines in |
1102 | | * constraint_expr_eval_reason will realloc in REASON_BUF_SIZE |
1103 | | * chunks (as it gets called for each mls/validatetrans processed). |
1104 | | * We just make sure these start from zero. |
1105 | | */ |
1106 | 0 | *reason_buf = NULL; |
1107 | 0 | reason_buf_used = 0; |
1108 | 0 | reason_buf_len = 0; |
1109 | 0 | constraint = tclass_datum->validatetrans; |
1110 | 0 | while (constraint) { |
1111 | 0 | if (!constraint_expr_eval_reason(ocontext, ncontext, tcontext, |
1112 | 0 | tclass, constraint, reason_buf, |
1113 | 0 | flags)) { |
1114 | 0 | return -EPERM; |
1115 | 0 | } |
1116 | 0 | constraint = constraint->next; |
1117 | 0 | } |
1118 | 0 | return 0; |
1119 | 0 | } |
1120 | | |
1121 | | int sepol_compute_av_reason(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1122 | | sepol_security_class_t tclass, |
1123 | | sepol_access_vector_t requested, |
1124 | | struct sepol_av_decision *avd, unsigned int *reason) |
1125 | 0 | { |
1126 | 0 | context_struct_t *scontext = NULL, *tcontext = NULL; |
1127 | 0 | int rc = 0; |
1128 | |
|
1129 | 0 | scontext = sepol_sidtab_search(sidtab, ssid); |
1130 | 0 | if (!scontext) { |
1131 | 0 | ERR(NULL, "unrecognized source SID %d", ssid); |
1132 | 0 | rc = -EINVAL; |
1133 | 0 | goto out; |
1134 | 0 | } |
1135 | 0 | tcontext = sepol_sidtab_search(sidtab, tsid); |
1136 | 0 | if (!tcontext) { |
1137 | 0 | ERR(NULL, "unrecognized target SID %d", tsid); |
1138 | 0 | rc = -EINVAL; |
1139 | 0 | goto out; |
1140 | 0 | } |
1141 | | |
1142 | 0 | rc = context_struct_compute_av(scontext, tcontext, tclass, requested, |
1143 | 0 | avd, reason, NULL, 0); |
1144 | 0 | out: |
1145 | 0 | return rc; |
1146 | 0 | } |
1147 | | |
1148 | | /* |
1149 | | * sepol_compute_av_reason_buffer - the reason buffer is malloc'd to |
1150 | | * REASON_BUF_SIZE. If the buffer size is exceeded, then it is realloc'd |
1151 | | * in the constraint_expr_eval_reason() function. |
1152 | | */ |
1153 | | int sepol_compute_av_reason_buffer(sepol_security_id_t ssid, |
1154 | | sepol_security_id_t tsid, |
1155 | | sepol_security_class_t tclass, |
1156 | | sepol_access_vector_t requested, |
1157 | | struct sepol_av_decision *avd, |
1158 | | unsigned int *reason, char **reason_buf, |
1159 | | unsigned int flags) |
1160 | 0 | { |
1161 | 0 | context_struct_t *scontext = NULL, *tcontext = NULL; |
1162 | 0 | int rc = 0; |
1163 | |
|
1164 | 0 | scontext = sepol_sidtab_search(sidtab, ssid); |
1165 | 0 | if (!scontext) { |
1166 | 0 | ERR(NULL, "unrecognized source SID %d", ssid); |
1167 | 0 | rc = -EINVAL; |
1168 | 0 | goto out; |
1169 | 0 | } |
1170 | 0 | tcontext = sepol_sidtab_search(sidtab, tsid); |
1171 | 0 | if (!tcontext) { |
1172 | 0 | ERR(NULL, "unrecognized target SID %d", tsid); |
1173 | 0 | rc = -EINVAL; |
1174 | 0 | goto out; |
1175 | 0 | } |
1176 | | |
1177 | | /* |
1178 | | * Set the buffer to NULL as constraints may not be processed. |
1179 | | * If a buffer is required, then the routines in |
1180 | | * constraint_expr_eval_reason will realloc in REASON_BUF_SIZE |
1181 | | * chunks (as it gets called for each constraint processed). |
1182 | | * We just make sure these start from zero. |
1183 | | */ |
1184 | 0 | *reason_buf = NULL; |
1185 | 0 | reason_buf_used = 0; |
1186 | 0 | reason_buf_len = 0; |
1187 | |
|
1188 | 0 | rc = context_struct_compute_av(scontext, tcontext, tclass, requested, |
1189 | 0 | avd, reason, reason_buf, flags); |
1190 | 0 | out: |
1191 | 0 | return rc; |
1192 | 0 | } |
1193 | | |
1194 | | int sepol_compute_av(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1195 | | sepol_security_class_t tclass, |
1196 | | sepol_access_vector_t requested, |
1197 | | struct sepol_av_decision *avd) |
1198 | 0 | { |
1199 | 0 | unsigned int reason = 0; |
1200 | 0 | return sepol_compute_av_reason(ssid, tsid, tclass, requested, avd, |
1201 | 0 | &reason); |
1202 | 0 | } |
1203 | | |
1204 | | /* |
1205 | | * Return a class ID associated with the class string specified by |
1206 | | * class_name. |
1207 | | */ |
1208 | | int sepol_string_to_security_class(const char *class_name, |
1209 | | sepol_security_class_t *tclass) |
1210 | 0 | { |
1211 | 0 | class_datum_t *tclass_datum; |
1212 | |
|
1213 | 0 | tclass_datum = hashtab_search(policydb->p_classes.table, class_name); |
1214 | 0 | if (!tclass_datum) { |
1215 | 0 | ERR(NULL, "unrecognized class %s", class_name); |
1216 | 0 | return STATUS_ERR; |
1217 | 0 | } |
1218 | 0 | *tclass = tclass_datum->s.value; |
1219 | 0 | return STATUS_SUCCESS; |
1220 | 0 | } |
1221 | | |
1222 | | /* |
1223 | | * Return access vector bit associated with the class ID and permission |
1224 | | * string. |
1225 | | */ |
1226 | | int sepol_string_to_av_perm(sepol_security_class_t tclass, |
1227 | | const char *perm_name, sepol_access_vector_t *av) |
1228 | 0 | { |
1229 | 0 | class_datum_t *tclass_datum; |
1230 | 0 | perm_datum_t *perm_datum; |
1231 | |
|
1232 | 0 | if (!tclass || tclass > policydb->p_classes.nprim) { |
1233 | 0 | ERR(NULL, "unrecognized class %d", tclass); |
1234 | 0 | return -EINVAL; |
1235 | 0 | } |
1236 | 0 | tclass_datum = policydb->class_val_to_struct[tclass - 1]; |
1237 | | |
1238 | | /* Check for unique perms then the common ones (if any) */ |
1239 | 0 | perm_datum = (perm_datum_t *)hashtab_search( |
1240 | 0 | tclass_datum->permissions.table, perm_name); |
1241 | 0 | if (perm_datum != NULL) { |
1242 | 0 | *av = UINT32_C(1) << (perm_datum->s.value - 1); |
1243 | 0 | return STATUS_SUCCESS; |
1244 | 0 | } |
1245 | | |
1246 | 0 | if (tclass_datum->comdatum == NULL) |
1247 | 0 | goto out; |
1248 | | |
1249 | 0 | perm_datum = (perm_datum_t *)hashtab_search( |
1250 | 0 | tclass_datum->comdatum->permissions.table, perm_name); |
1251 | |
|
1252 | 0 | if (perm_datum != NULL) { |
1253 | 0 | *av = UINT32_C(1) << (perm_datum->s.value - 1); |
1254 | 0 | return STATUS_SUCCESS; |
1255 | 0 | } |
1256 | 0 | out: |
1257 | 0 | ERR(NULL, "could not convert %s to av bit", perm_name); |
1258 | 0 | return STATUS_ERR; |
1259 | 0 | } |
1260 | | |
1261 | | const char *sepol_av_perm_to_string(sepol_security_class_t tclass, |
1262 | | sepol_access_vector_t av) |
1263 | 0 | { |
1264 | 0 | static char avbuf[1024]; |
1265 | 0 | char *avstr = sepol_av_to_string(policydb, tclass, av); |
1266 | 0 | size_t len; |
1267 | |
|
1268 | 0 | memset(avbuf, 0, sizeof(avbuf)); |
1269 | |
|
1270 | 0 | if (avstr) { |
1271 | 0 | len = strlen(avstr); |
1272 | 0 | if (len < sizeof(avbuf)) { |
1273 | 0 | strcpy(avbuf, avstr); |
1274 | 0 | } else { |
1275 | 0 | sprintf(avbuf, "<access-vector overflowed buffer>"); |
1276 | 0 | } |
1277 | 0 | free(avstr); |
1278 | 0 | } else { |
1279 | 0 | sprintf(avbuf, "<format-failure>"); |
1280 | 0 | } |
1281 | |
|
1282 | 0 | return avbuf; |
1283 | 0 | } |
1284 | | |
1285 | | /* |
1286 | | * Write the security context string representation of |
1287 | | * the context associated with `sid' into a dynamically |
1288 | | * allocated string of the correct size. Set `*scontext' |
1289 | | * to point to this string and set `*scontext_len' to |
1290 | | * the length of the string. |
1291 | | */ |
1292 | | int sepol_sid_to_context(sepol_security_id_t sid, |
1293 | | sepol_security_context_t *scontext, |
1294 | | size_t *scontext_len) |
1295 | 0 | { |
1296 | 0 | context_struct_t *context; |
1297 | 0 | int rc = 0; |
1298 | |
|
1299 | 0 | context = sepol_sidtab_search(sidtab, sid); |
1300 | 0 | if (!context) { |
1301 | 0 | ERR(NULL, "unrecognized SID %d", sid); |
1302 | 0 | rc = -EINVAL; |
1303 | 0 | goto out; |
1304 | 0 | } |
1305 | 0 | rc = context_to_string(NULL, policydb, context, scontext, scontext_len); |
1306 | 0 | out: |
1307 | 0 | return rc; |
1308 | 0 | } |
1309 | | |
1310 | | /* |
1311 | | * Return a SID associated with the security context that |
1312 | | * has the string representation specified by `scontext'. |
1313 | | */ |
1314 | | int sepol_context_to_sid(sepol_const_security_context_t scontext, |
1315 | | size_t scontext_len, sepol_security_id_t *sid) |
1316 | 0 | { |
1317 | 0 | context_struct_t *context = NULL; |
1318 | | |
1319 | | /* First, create the context */ |
1320 | 0 | if (context_from_string(NULL, policydb, &context, scontext, |
1321 | 0 | scontext_len) < 0) |
1322 | 0 | goto err; |
1323 | | |
1324 | | /* Obtain the new sid */ |
1325 | 0 | if (sid && (sepol_sidtab_context_to_sid(sidtab, context, sid) < 0)) |
1326 | 0 | goto err; |
1327 | | |
1328 | 0 | context_destroy(context); |
1329 | 0 | free(context); |
1330 | 0 | return STATUS_SUCCESS; |
1331 | | |
1332 | 0 | err: |
1333 | 0 | if (context) { |
1334 | 0 | context_destroy(context); |
1335 | 0 | free(context); |
1336 | 0 | } |
1337 | 0 | ERR(NULL, "could not convert %s to sid", scontext); |
1338 | 0 | return STATUS_ERR; |
1339 | 0 | } |
1340 | | |
1341 | | static inline int compute_sid_handle_invalid_context( |
1342 | | context_struct_t *scontext, context_struct_t *tcontext, |
1343 | | sepol_security_class_t tclass, context_struct_t *newcontext) |
1344 | 0 | { |
1345 | 0 | if (selinux_enforcing) { |
1346 | 0 | return -EACCES; |
1347 | 0 | } else { |
1348 | 0 | sepol_security_context_t s, t, n; |
1349 | 0 | size_t slen, tlen, nlen; |
1350 | |
|
1351 | 0 | context_to_string(NULL, policydb, scontext, &s, &slen); |
1352 | 0 | context_to_string(NULL, policydb, tcontext, &t, &tlen); |
1353 | 0 | context_to_string(NULL, policydb, newcontext, &n, &nlen); |
1354 | 0 | ERR(NULL, |
1355 | 0 | "invalid context %s for " |
1356 | 0 | "scontext=%s tcontext=%s tclass=%s", |
1357 | 0 | n, s, t, policydb->p_class_val_to_name[tclass - 1]); |
1358 | 0 | free(s); |
1359 | 0 | free(t); |
1360 | 0 | free(n); |
1361 | 0 | return 0; |
1362 | 0 | } |
1363 | 0 | } |
1364 | | |
1365 | | static int sepol_compute_sid(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1366 | | sepol_security_class_t tclass, uint32_t specified, |
1367 | | sepol_security_id_t *out_sid) |
1368 | 0 | { |
1369 | 0 | struct class_datum *cladatum = NULL; |
1370 | 0 | context_struct_t *scontext = NULL, *tcontext = NULL, newcontext; |
1371 | 0 | struct role_trans *roletr = NULL; |
1372 | 0 | avtab_key_t avkey; |
1373 | 0 | avtab_datum_t *avdatum; |
1374 | 0 | avtab_ptr_t node; |
1375 | 0 | int rc = 0; |
1376 | |
|
1377 | 0 | scontext = sepol_sidtab_search(sidtab, ssid); |
1378 | 0 | if (!scontext) { |
1379 | 0 | ERR(NULL, "unrecognized SID %d", ssid); |
1380 | 0 | return -EINVAL; |
1381 | 0 | } |
1382 | 0 | tcontext = sepol_sidtab_search(sidtab, tsid); |
1383 | 0 | if (!tcontext) { |
1384 | 0 | ERR(NULL, "unrecognized SID %d", tsid); |
1385 | 0 | return -EINVAL; |
1386 | 0 | } |
1387 | | |
1388 | 0 | if (tclass && tclass <= policydb->p_classes.nprim) |
1389 | 0 | cladatum = policydb->class_val_to_struct[tclass - 1]; |
1390 | |
|
1391 | 0 | context_init(&newcontext); |
1392 | | |
1393 | | /* Set the user identity. */ |
1394 | 0 | switch (specified) { |
1395 | 0 | case AVTAB_TRANSITION: |
1396 | 0 | case AVTAB_CHANGE: |
1397 | 0 | if (cladatum && cladatum->default_user == DEFAULT_TARGET) { |
1398 | 0 | newcontext.user = tcontext->user; |
1399 | 0 | } else { |
1400 | | /* notice this gets both DEFAULT_SOURCE and unset */ |
1401 | | /* Use the process user identity. */ |
1402 | 0 | newcontext.user = scontext->user; |
1403 | 0 | } |
1404 | 0 | break; |
1405 | 0 | case AVTAB_MEMBER: |
1406 | | /* Use the related object owner. */ |
1407 | 0 | newcontext.user = tcontext->user; |
1408 | 0 | break; |
1409 | 0 | } |
1410 | | |
1411 | | /* Set the role to default values. */ |
1412 | 0 | if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { |
1413 | 0 | newcontext.role = scontext->role; |
1414 | 0 | } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { |
1415 | 0 | newcontext.role = tcontext->role; |
1416 | 0 | } else { |
1417 | 0 | if (tclass == policydb->process_class) |
1418 | 0 | newcontext.role = scontext->role; |
1419 | 0 | else |
1420 | 0 | newcontext.role = OBJECT_R_VAL; |
1421 | 0 | } |
1422 | | |
1423 | | /* Set the type to default values. */ |
1424 | 0 | if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { |
1425 | 0 | newcontext.type = scontext->type; |
1426 | 0 | } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { |
1427 | 0 | newcontext.type = tcontext->type; |
1428 | 0 | } else { |
1429 | 0 | if (tclass == policydb->process_class) { |
1430 | | /* Use the type of process. */ |
1431 | 0 | newcontext.type = scontext->type; |
1432 | 0 | } else { |
1433 | | /* Use the type of the related object. */ |
1434 | 0 | newcontext.type = tcontext->type; |
1435 | 0 | } |
1436 | 0 | } |
1437 | | |
1438 | | /* Look for a type transition/member/change rule. */ |
1439 | 0 | avkey.source_type = scontext->type; |
1440 | 0 | avkey.target_type = tcontext->type; |
1441 | 0 | avkey.target_class = tclass; |
1442 | 0 | avkey.specified = specified; |
1443 | 0 | avdatum = avtab_search(&policydb->te_avtab, &avkey); |
1444 | | |
1445 | | /* If no permanent rule, also check for enabled conditional rules */ |
1446 | 0 | if (!avdatum) { |
1447 | 0 | node = avtab_search_node(&policydb->te_cond_avtab, &avkey); |
1448 | 0 | for (; node != NULL; |
1449 | 0 | node = avtab_search_node_next(node, specified)) { |
1450 | 0 | if (node->key.specified & AVTAB_ENABLED) { |
1451 | 0 | avdatum = &node->datum; |
1452 | 0 | break; |
1453 | 0 | } |
1454 | 0 | } |
1455 | 0 | } |
1456 | |
|
1457 | 0 | if (avdatum) { |
1458 | | /* Use the type from the type transition/member/change rule. */ |
1459 | 0 | newcontext.type = avdatum->data; |
1460 | 0 | } |
1461 | | |
1462 | | /* Check for class-specific changes. */ |
1463 | 0 | if (specified & AVTAB_TRANSITION) { |
1464 | | /* Look for a role transition rule. */ |
1465 | 0 | for (roletr = policydb->role_tr; roletr; |
1466 | 0 | roletr = roletr->next) { |
1467 | 0 | if (roletr->role == scontext->role && |
1468 | 0 | roletr->type == tcontext->type && |
1469 | 0 | roletr->tclass == tclass) { |
1470 | | /* Use the role transition rule. */ |
1471 | 0 | newcontext.role = roletr->new_role; |
1472 | 0 | break; |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | } |
1476 | | |
1477 | | /* Set the MLS attributes. |
1478 | | This is done last because it may allocate memory. */ |
1479 | 0 | rc = mls_compute_sid(policydb, scontext, tcontext, tclass, specified, |
1480 | 0 | &newcontext); |
1481 | 0 | if (rc) |
1482 | 0 | goto out; |
1483 | | |
1484 | | /* Check the validity of the context. */ |
1485 | 0 | if (!policydb_context_isvalid(policydb, &newcontext)) { |
1486 | 0 | rc = compute_sid_handle_invalid_context(scontext, tcontext, |
1487 | 0 | tclass, &newcontext); |
1488 | 0 | if (rc) |
1489 | 0 | goto out; |
1490 | 0 | } |
1491 | | /* Obtain the sid for the context. */ |
1492 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &newcontext, out_sid); |
1493 | 0 | out: |
1494 | 0 | context_destroy(&newcontext); |
1495 | 0 | return rc; |
1496 | 0 | } |
1497 | | |
1498 | | /* |
1499 | | * Compute a SID to use for labeling a new object in the |
1500 | | * class `tclass' based on a SID pair. |
1501 | | */ |
1502 | | int sepol_transition_sid(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1503 | | sepol_security_class_t tclass, |
1504 | | sepol_security_id_t *out_sid) |
1505 | 0 | { |
1506 | 0 | return sepol_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); |
1507 | 0 | } |
1508 | | |
1509 | | /* |
1510 | | * Compute a SID to use when selecting a member of a |
1511 | | * polyinstantiated object of class `tclass' based on |
1512 | | * a SID pair. |
1513 | | */ |
1514 | | int sepol_member_sid(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1515 | | sepol_security_class_t tclass, |
1516 | | sepol_security_id_t *out_sid) |
1517 | 0 | { |
1518 | 0 | return sepol_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); |
1519 | 0 | } |
1520 | | |
1521 | | /* |
1522 | | * Compute a SID to use for relabeling an object in the |
1523 | | * class `tclass' based on a SID pair. |
1524 | | */ |
1525 | | int sepol_change_sid(sepol_security_id_t ssid, sepol_security_id_t tsid, |
1526 | | sepol_security_class_t tclass, |
1527 | | sepol_security_id_t *out_sid) |
1528 | 0 | { |
1529 | 0 | return sepol_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); |
1530 | 0 | } |
1531 | | |
1532 | | /* |
1533 | | * Verify that each permission that is defined under the |
1534 | | * existing policy is still defined with the same value |
1535 | | * in the new policy. |
1536 | | */ |
1537 | | static int validate_perm(hashtab_key_t key, hashtab_datum_t datum, void *p) |
1538 | 0 | { |
1539 | 0 | hashtab_t h; |
1540 | 0 | perm_datum_t *perdatum, *perdatum2; |
1541 | |
|
1542 | 0 | h = (hashtab_t)p; |
1543 | 0 | perdatum = (perm_datum_t *)datum; |
1544 | |
|
1545 | 0 | perdatum2 = (perm_datum_t *)hashtab_search(h, key); |
1546 | 0 | if (!perdatum2) { |
1547 | 0 | ERR(NULL, "permission %s disappeared", key); |
1548 | 0 | return -1; |
1549 | 0 | } |
1550 | 0 | if (perdatum->s.value != perdatum2->s.value) { |
1551 | 0 | ERR(NULL, "the value of permissions %s changed", key); |
1552 | 0 | return -1; |
1553 | 0 | } |
1554 | 0 | return 0; |
1555 | 0 | } |
1556 | | |
1557 | | /* |
1558 | | * Verify that each class that is defined under the |
1559 | | * existing policy is still defined with the same |
1560 | | * attributes in the new policy. |
1561 | | */ |
1562 | | static int validate_class(hashtab_key_t key, hashtab_datum_t datum, void *p) |
1563 | 0 | { |
1564 | 0 | policydb_t *newp; |
1565 | 0 | class_datum_t *cladatum, *cladatum2; |
1566 | |
|
1567 | 0 | newp = (policydb_t *)p; |
1568 | 0 | cladatum = (class_datum_t *)datum; |
1569 | |
|
1570 | 0 | cladatum2 = (class_datum_t *)hashtab_search(newp->p_classes.table, key); |
1571 | 0 | if (!cladatum2) { |
1572 | 0 | ERR(NULL, "class %s disappeared", key); |
1573 | 0 | return -1; |
1574 | 0 | } |
1575 | 0 | if (cladatum->s.value != cladatum2->s.value) { |
1576 | 0 | ERR(NULL, "the value of class %s changed", key); |
1577 | 0 | return -1; |
1578 | 0 | } |
1579 | 0 | if ((cladatum->comdatum && !cladatum2->comdatum) || |
1580 | 0 | (!cladatum->comdatum && cladatum2->comdatum)) { |
1581 | 0 | ERR(NULL, |
1582 | 0 | "the inherits clause for the access " |
1583 | 0 | "vector definition for class %s changed", |
1584 | 0 | key); |
1585 | 0 | return -1; |
1586 | 0 | } |
1587 | 0 | if (cladatum->comdatum) { |
1588 | 0 | if (hashtab_map(cladatum->comdatum->permissions.table, |
1589 | 0 | validate_perm, |
1590 | 0 | cladatum2->comdatum->permissions.table)) { |
1591 | 0 | ERR(NULL, |
1592 | 0 | " in the access vector definition " |
1593 | 0 | "for class %s", |
1594 | 0 | key); |
1595 | 0 | return -1; |
1596 | 0 | } |
1597 | 0 | } |
1598 | 0 | if (hashtab_map(cladatum->permissions.table, validate_perm, |
1599 | 0 | cladatum2->permissions.table)) { |
1600 | 0 | ERR(NULL, " in access vector definition for class %s", key); |
1601 | 0 | return -1; |
1602 | 0 | } |
1603 | 0 | return 0; |
1604 | 0 | } |
1605 | | |
1606 | | /* Clone the SID into the new SID table. */ |
1607 | | static int clone_sid(sepol_security_id_t sid, context_struct_t *context, |
1608 | | void *arg) |
1609 | 0 | { |
1610 | 0 | sidtab_t *s = arg; |
1611 | |
|
1612 | 0 | return sepol_sidtab_insert(s, sid, context); |
1613 | 0 | } |
1614 | | |
1615 | | static inline int |
1616 | | convert_context_handle_invalid_context(context_struct_t *context) |
1617 | 0 | { |
1618 | 0 | if (selinux_enforcing) { |
1619 | 0 | return -EINVAL; |
1620 | 0 | } else { |
1621 | 0 | sepol_security_context_t s; |
1622 | 0 | size_t len; |
1623 | |
|
1624 | 0 | context_to_string(NULL, policydb, context, &s, &len); |
1625 | 0 | ERR(NULL, "context %s is invalid", s); |
1626 | 0 | free(s); |
1627 | 0 | return 0; |
1628 | 0 | } |
1629 | 0 | } |
1630 | | |
1631 | | typedef struct { |
1632 | | policydb_t *oldp; |
1633 | | policydb_t *newp; |
1634 | | } convert_context_args_t; |
1635 | | |
1636 | | /* |
1637 | | * Convert the values in the security context |
1638 | | * structure `c' from the values specified |
1639 | | * in the policy `p->oldp' to the values specified |
1640 | | * in the policy `p->newp'. Verify that the |
1641 | | * context is valid under the new policy. |
1642 | | */ |
1643 | | static int convert_context(sepol_security_id_t key __attribute__((unused)), |
1644 | | context_struct_t *c, void *p) |
1645 | 0 | { |
1646 | 0 | convert_context_args_t *args; |
1647 | 0 | context_struct_t oldc; |
1648 | 0 | role_datum_t *role; |
1649 | 0 | type_datum_t *typdatum; |
1650 | 0 | user_datum_t *usrdatum; |
1651 | 0 | sepol_security_context_t s; |
1652 | 0 | size_t len; |
1653 | 0 | int rc = -EINVAL; |
1654 | |
|
1655 | 0 | args = (convert_context_args_t *)p; |
1656 | |
|
1657 | 0 | if (context_cpy(&oldc, c)) |
1658 | 0 | return -ENOMEM; |
1659 | | |
1660 | | /* Convert the user. */ |
1661 | 0 | usrdatum = (user_datum_t *)hashtab_search( |
1662 | 0 | args->newp->p_users.table, |
1663 | 0 | args->oldp->p_user_val_to_name[c->user - 1]); |
1664 | |
|
1665 | 0 | if (!usrdatum) { |
1666 | 0 | goto bad; |
1667 | 0 | } |
1668 | 0 | c->user = usrdatum->s.value; |
1669 | | |
1670 | | /* Convert the role. */ |
1671 | 0 | role = (role_datum_t *)hashtab_search( |
1672 | 0 | args->newp->p_roles.table, |
1673 | 0 | args->oldp->p_role_val_to_name[c->role - 1]); |
1674 | 0 | if (!role) { |
1675 | 0 | goto bad; |
1676 | 0 | } |
1677 | 0 | c->role = role->s.value; |
1678 | | |
1679 | | /* Convert the type. */ |
1680 | 0 | typdatum = (type_datum_t *)hashtab_search( |
1681 | 0 | args->newp->p_types.table, |
1682 | 0 | args->oldp->p_type_val_to_name[c->type - 1]); |
1683 | 0 | if (!typdatum) { |
1684 | 0 | goto bad; |
1685 | 0 | } |
1686 | 0 | c->type = typdatum->s.value; |
1687 | |
|
1688 | 0 | rc = mls_convert_context(args->oldp, args->newp, c); |
1689 | 0 | if (rc) |
1690 | 0 | goto bad; |
1691 | | |
1692 | | /* Check the validity of the new context. */ |
1693 | 0 | if (!policydb_context_isvalid(args->newp, c)) { |
1694 | 0 | rc = convert_context_handle_invalid_context(&oldc); |
1695 | 0 | if (rc) |
1696 | 0 | goto bad; |
1697 | 0 | } |
1698 | | |
1699 | 0 | context_destroy(&oldc); |
1700 | 0 | return 0; |
1701 | | |
1702 | 0 | bad: |
1703 | 0 | context_to_string(NULL, policydb, &oldc, &s, &len); |
1704 | 0 | context_destroy(&oldc); |
1705 | 0 | ERR(NULL, "invalidating context %s", s); |
1706 | 0 | free(s); |
1707 | 0 | return rc; |
1708 | 0 | } |
1709 | | |
1710 | | /* Reading from a policy "file". */ |
1711 | | int next_entry(void *buf, struct policy_file *fp, size_t bytes) |
1712 | 2.87M | { |
1713 | 2.87M | size_t nread; |
1714 | | |
1715 | 2.87M | switch (fp->type) { |
1716 | 0 | case PF_USE_STDIO: |
1717 | 0 | nread = fread(buf, bytes, 1, fp->fp); |
1718 | |
|
1719 | 0 | if (nread != 1) |
1720 | 0 | return -1; |
1721 | 0 | break; |
1722 | 2.87M | case PF_USE_MEMORY: |
1723 | 2.87M | if (bytes > fp->len) { |
1724 | 5.00k | errno = EOVERFLOW; |
1725 | 5.00k | return -1; |
1726 | 5.00k | } |
1727 | 2.86M | memcpy(buf, fp->data, bytes); |
1728 | 2.86M | fp->data += bytes; |
1729 | 2.86M | fp->len -= bytes; |
1730 | 2.86M | break; |
1731 | 0 | default: |
1732 | 0 | errno = EINVAL; |
1733 | 0 | return -1; |
1734 | 2.87M | } |
1735 | 2.86M | return 0; |
1736 | 2.87M | } |
1737 | | |
1738 | | size_t put_entry(const void *ptr, size_t size, size_t n, struct policy_file *fp) |
1739 | 1.36M | { |
1740 | 1.36M | size_t bytes; |
1741 | | |
1742 | 1.36M | if (__builtin_mul_overflow(size, n, &bytes)) |
1743 | 0 | return 0; |
1744 | | |
1745 | 1.36M | switch (fp->type) { |
1746 | 1.36M | case PF_USE_STDIO: |
1747 | 1.36M | return fwrite(ptr, size, n, fp->fp); |
1748 | 0 | case PF_USE_MEMORY: |
1749 | 0 | if (bytes > fp->len) { |
1750 | 0 | errno = ENOSPC; |
1751 | 0 | return 0; |
1752 | 0 | } |
1753 | | |
1754 | 0 | memcpy(fp->data, ptr, bytes); |
1755 | 0 | fp->data += bytes; |
1756 | 0 | fp->len -= bytes; |
1757 | 0 | return n; |
1758 | 0 | case PF_LEN: |
1759 | 0 | fp->len += bytes; |
1760 | 0 | return n; |
1761 | 0 | default: |
1762 | 0 | return 0; |
1763 | 1.36M | } |
1764 | 0 | return 0; |
1765 | 1.36M | } |
1766 | | |
1767 | | /* |
1768 | | * Reads a string and null terminates it from the policy file. |
1769 | | * This is a port of str_read from the SE Linux kernel code. |
1770 | | * |
1771 | | * It returns: |
1772 | | * 0 - Success |
1773 | | * -1 - Failure with errno set |
1774 | | */ |
1775 | | int str_read(char **strp, struct policy_file *fp, size_t len) |
1776 | 507k | { |
1777 | 507k | int rc; |
1778 | 507k | char *str; |
1779 | | |
1780 | 507k | if (zero_or_saturated(len) || |
1781 | 507k | exceeds_available_bytes(fp, len, sizeof(char))) { |
1782 | 220 | errno = EINVAL; |
1783 | 220 | return -1; |
1784 | 220 | } |
1785 | | |
1786 | 506k | str = malloc(len + 1); |
1787 | 506k | if (!str) |
1788 | 0 | return -1; |
1789 | | |
1790 | | /* it's expected the caller should free the str */ |
1791 | 506k | *strp = str; |
1792 | | |
1793 | | /* next_entry sets errno */ |
1794 | 506k | rc = next_entry(str, fp, len); |
1795 | 506k | if (rc) |
1796 | 0 | return rc; |
1797 | | |
1798 | | /* Check for an embedded NUL character */ |
1799 | 506k | if (memchr(str, '\0', len)) { |
1800 | 38 | errno = EINVAL; |
1801 | 38 | return -1; |
1802 | 38 | } |
1803 | | |
1804 | 506k | str[len] = '\0'; |
1805 | 506k | return 0; |
1806 | 506k | } |
1807 | | |
1808 | | /* |
1809 | | * Read a new set of configuration data from |
1810 | | * a policy database binary representation file. |
1811 | | * |
1812 | | * Verify that each class that is defined under the |
1813 | | * existing policy is still defined with the same |
1814 | | * attributes in the new policy. |
1815 | | * |
1816 | | * Convert the context structures in the SID table to the |
1817 | | * new representation and verify that all entries |
1818 | | * in the SID table are valid under the new policy. |
1819 | | * |
1820 | | * Change the active policy database to use the new |
1821 | | * configuration data. |
1822 | | * |
1823 | | * Reset the access vector cache. |
1824 | | */ |
1825 | | int sepol_load_policy(void *data, size_t len) |
1826 | 0 | { |
1827 | 0 | policydb_t oldpolicydb, newpolicydb; |
1828 | 0 | sidtab_t oldsidtab, newsidtab; |
1829 | 0 | convert_context_args_t args; |
1830 | 0 | int rc = 0; |
1831 | 0 | struct policy_file file, *fp; |
1832 | |
|
1833 | 0 | policy_file_init(&file); |
1834 | 0 | file.type = PF_USE_MEMORY; |
1835 | 0 | file.data = data; |
1836 | 0 | file.len = len; |
1837 | 0 | fp = &file; |
1838 | |
|
1839 | 0 | if (policydb_init(&newpolicydb)) |
1840 | 0 | return -ENOMEM; |
1841 | | |
1842 | 0 | if (policydb_read(&newpolicydb, fp, 1)) { |
1843 | 0 | policydb_destroy(&mypolicydb); |
1844 | 0 | return -EINVAL; |
1845 | 0 | } |
1846 | | |
1847 | 0 | sepol_sidtab_init(&newsidtab); |
1848 | | |
1849 | | /* Verify that the existing classes did not change. */ |
1850 | 0 | if (hashtab_map(policydb->p_classes.table, validate_class, |
1851 | 0 | &newpolicydb)) { |
1852 | 0 | ERR(NULL, "the definition of an existing class changed"); |
1853 | 0 | rc = -EINVAL; |
1854 | 0 | goto err; |
1855 | 0 | } |
1856 | | |
1857 | | /* Clone the SID table. */ |
1858 | 0 | sepol_sidtab_shutdown(sidtab); |
1859 | 0 | if (sepol_sidtab_map(sidtab, clone_sid, &newsidtab)) { |
1860 | 0 | rc = -ENOMEM; |
1861 | 0 | goto err; |
1862 | 0 | } |
1863 | | |
1864 | | /* Convert the internal representations of contexts |
1865 | | in the new SID table and remove invalid SIDs. */ |
1866 | 0 | args.oldp = policydb; |
1867 | 0 | args.newp = &newpolicydb; |
1868 | 0 | sepol_sidtab_map_remove_on_error(&newsidtab, convert_context, &args); |
1869 | | |
1870 | | /* Save the old policydb and SID table to free later. */ |
1871 | 0 | memcpy(&oldpolicydb, policydb, sizeof *policydb); |
1872 | 0 | sepol_sidtab_set(&oldsidtab, sidtab); |
1873 | | |
1874 | | /* Install the new policydb and SID table. */ |
1875 | 0 | memcpy(policydb, &newpolicydb, sizeof *policydb); |
1876 | 0 | sepol_sidtab_set(sidtab, &newsidtab); |
1877 | | |
1878 | | /* Free the old policydb and SID table. */ |
1879 | 0 | policydb_destroy(&oldpolicydb); |
1880 | 0 | sepol_sidtab_destroy(&oldsidtab); |
1881 | |
|
1882 | 0 | return 0; |
1883 | | |
1884 | 0 | err: |
1885 | 0 | sepol_sidtab_destroy(&newsidtab); |
1886 | 0 | policydb_destroy(&newpolicydb); |
1887 | 0 | return rc; |
1888 | 0 | } |
1889 | | |
1890 | | /* |
1891 | | * Return the SIDs to use for an unlabeled file system |
1892 | | * that is being mounted from the device with the |
1893 | | * the kdevname `name'. The `fs_sid' SID is returned for |
1894 | | * the file system and the `file_sid' SID is returned |
1895 | | * for all files within that file system. |
1896 | | */ |
1897 | | int sepol_fs_sid(char *name, sepol_security_id_t *fs_sid, |
1898 | | sepol_security_id_t *file_sid) |
1899 | 0 | { |
1900 | 0 | int rc = 0; |
1901 | 0 | ocontext_t *c; |
1902 | |
|
1903 | 0 | c = policydb->ocontexts[OCON_FS]; |
1904 | 0 | while (c) { |
1905 | 0 | if (strcmp(c->u.name, name) == 0) |
1906 | 0 | break; |
1907 | 0 | c = c->next; |
1908 | 0 | } |
1909 | |
|
1910 | 0 | if (c) { |
1911 | 0 | if (!c->sid[0] || !c->sid[1]) { |
1912 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
1913 | 0 | &c->sid[0]); |
1914 | 0 | if (rc) |
1915 | 0 | goto out; |
1916 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[1], |
1917 | 0 | &c->sid[1]); |
1918 | 0 | if (rc) |
1919 | 0 | goto out; |
1920 | 0 | } |
1921 | 0 | *fs_sid = c->sid[0]; |
1922 | 0 | *file_sid = c->sid[1]; |
1923 | 0 | } else { |
1924 | 0 | *fs_sid = SECINITSID_FS; |
1925 | 0 | *file_sid = SECINITSID_FILE; |
1926 | 0 | } |
1927 | | |
1928 | 0 | out: |
1929 | 0 | return rc; |
1930 | 0 | } |
1931 | | |
1932 | | /* |
1933 | | * Return the SID of the ibpkey specified by |
1934 | | * `subnet prefix', and `pkey number'. |
1935 | | */ |
1936 | | int sepol_ibpkey_sid(uint64_t subnet_prefix, uint16_t pkey, |
1937 | | sepol_security_id_t *out_sid) |
1938 | 0 | { |
1939 | 0 | ocontext_t *c; |
1940 | 0 | int rc = 0; |
1941 | |
|
1942 | 0 | c = policydb->ocontexts[OCON_IBPKEY]; |
1943 | 0 | while (c) { |
1944 | 0 | if (c->u.ibpkey.low_pkey <= pkey && |
1945 | 0 | c->u.ibpkey.high_pkey >= pkey && |
1946 | 0 | subnet_prefix == c->u.ibpkey.subnet_prefix) |
1947 | 0 | break; |
1948 | 0 | c = c->next; |
1949 | 0 | } |
1950 | |
|
1951 | 0 | if (c) { |
1952 | 0 | if (!c->sid[0]) { |
1953 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
1954 | 0 | &c->sid[0]); |
1955 | 0 | if (rc) |
1956 | 0 | goto out; |
1957 | 0 | } |
1958 | 0 | *out_sid = c->sid[0]; |
1959 | 0 | } else { |
1960 | 0 | *out_sid = SECINITSID_UNLABELED; |
1961 | 0 | } |
1962 | | |
1963 | 0 | out: |
1964 | 0 | return rc; |
1965 | 0 | } |
1966 | | |
1967 | | /* |
1968 | | * Return the SID of the subnet management interface specified by |
1969 | | * `device name', and `port'. |
1970 | | */ |
1971 | | int sepol_ibendport_sid(char *dev_name, uint8_t port, |
1972 | | sepol_security_id_t *out_sid) |
1973 | 0 | { |
1974 | 0 | ocontext_t *c; |
1975 | 0 | int rc = 0; |
1976 | |
|
1977 | 0 | c = policydb->ocontexts[OCON_IBENDPORT]; |
1978 | 0 | while (c) { |
1979 | 0 | if (c->u.ibendport.port == port && |
1980 | 0 | !strcmp(dev_name, c->u.ibendport.dev_name)) |
1981 | 0 | break; |
1982 | 0 | c = c->next; |
1983 | 0 | } |
1984 | |
|
1985 | 0 | if (c) { |
1986 | 0 | if (!c->sid[0]) { |
1987 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
1988 | 0 | &c->sid[0]); |
1989 | 0 | if (rc) |
1990 | 0 | goto out; |
1991 | 0 | } |
1992 | 0 | *out_sid = c->sid[0]; |
1993 | 0 | } else { |
1994 | 0 | *out_sid = SECINITSID_UNLABELED; |
1995 | 0 | } |
1996 | | |
1997 | 0 | out: |
1998 | 0 | return rc; |
1999 | 0 | } |
2000 | | |
2001 | | /* |
2002 | | * Return the SID of the port specified by |
2003 | | * `domain', `type', `protocol', and `port'. |
2004 | | */ |
2005 | | int sepol_port_sid(uint16_t domain __attribute__((unused)), |
2006 | | uint16_t type __attribute__((unused)), uint8_t protocol, |
2007 | | uint16_t port, sepol_security_id_t *out_sid) |
2008 | 0 | { |
2009 | 0 | ocontext_t *c; |
2010 | 0 | int rc = 0; |
2011 | |
|
2012 | 0 | c = policydb->ocontexts[OCON_PORT]; |
2013 | 0 | while (c) { |
2014 | 0 | if (c->u.port.protocol == protocol && |
2015 | 0 | c->u.port.low_port <= port && c->u.port.high_port >= port) |
2016 | 0 | break; |
2017 | 0 | c = c->next; |
2018 | 0 | } |
2019 | |
|
2020 | 0 | if (c) { |
2021 | 0 | if (!c->sid[0]) { |
2022 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
2023 | 0 | &c->sid[0]); |
2024 | 0 | if (rc) |
2025 | 0 | goto out; |
2026 | 0 | } |
2027 | 0 | *out_sid = c->sid[0]; |
2028 | 0 | } else { |
2029 | 0 | *out_sid = SECINITSID_PORT; |
2030 | 0 | } |
2031 | | |
2032 | 0 | out: |
2033 | 0 | return rc; |
2034 | 0 | } |
2035 | | |
2036 | | /* |
2037 | | * Return the SIDs to use for a network interface |
2038 | | * with the name `name'. The `if_sid' SID is returned for |
2039 | | * the interface and the `msg_sid' SID is returned as |
2040 | | * the default SID for messages received on the |
2041 | | * interface. |
2042 | | */ |
2043 | | int sepol_netif_sid(char *name, sepol_security_id_t *if_sid, |
2044 | | sepol_security_id_t *msg_sid) |
2045 | 0 | { |
2046 | 0 | int rc = 0; |
2047 | 0 | ocontext_t *c; |
2048 | |
|
2049 | 0 | c = policydb->ocontexts[OCON_NETIF]; |
2050 | 0 | while (c) { |
2051 | 0 | if (strcmp(name, c->u.name) == 0) |
2052 | 0 | break; |
2053 | 0 | c = c->next; |
2054 | 0 | } |
2055 | |
|
2056 | 0 | if (c) { |
2057 | 0 | if (!c->sid[0] || !c->sid[1]) { |
2058 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
2059 | 0 | &c->sid[0]); |
2060 | 0 | if (rc) |
2061 | 0 | goto out; |
2062 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[1], |
2063 | 0 | &c->sid[1]); |
2064 | 0 | if (rc) |
2065 | 0 | goto out; |
2066 | 0 | } |
2067 | 0 | *if_sid = c->sid[0]; |
2068 | 0 | *msg_sid = c->sid[1]; |
2069 | 0 | } else { |
2070 | 0 | *if_sid = SECINITSID_NETIF; |
2071 | 0 | *msg_sid = SECINITSID_NETMSG; |
2072 | 0 | } |
2073 | | |
2074 | 0 | out: |
2075 | 0 | return rc; |
2076 | 0 | } |
2077 | | |
2078 | | static int match_ipv6_addrmask(uint32_t *input, uint32_t *addr, uint32_t *mask) |
2079 | 0 | { |
2080 | 0 | int i, fail = 0; |
2081 | |
|
2082 | 0 | for (i = 0; i < 4; i++) |
2083 | 0 | if (addr[i] != (input[i] & mask[i])) { |
2084 | 0 | fail = 1; |
2085 | 0 | break; |
2086 | 0 | } |
2087 | |
|
2088 | 0 | return !fail; |
2089 | 0 | } |
2090 | | |
2091 | | /* |
2092 | | * Return the SID of the node specified by the address |
2093 | | * `addrp' where `addrlen' is the length of the address |
2094 | | * in bytes and `domain' is the communications domain or |
2095 | | * address family in which the address should be interpreted. |
2096 | | */ |
2097 | | int sepol_node_sid(uint16_t domain, void *addrp, size_t addrlen, |
2098 | | sepol_security_id_t *out_sid) |
2099 | 0 | { |
2100 | 0 | int rc = 0; |
2101 | 0 | ocontext_t *c; |
2102 | |
|
2103 | 0 | switch (domain) { |
2104 | 0 | case AF_INET: { |
2105 | 0 | uint32_t addr; |
2106 | |
|
2107 | 0 | if (addrlen != sizeof(uint32_t)) { |
2108 | 0 | rc = -EINVAL; |
2109 | 0 | goto out; |
2110 | 0 | } |
2111 | | |
2112 | 0 | addr = *((uint32_t *)addrp); |
2113 | |
|
2114 | 0 | c = policydb->ocontexts[OCON_NODE]; |
2115 | 0 | while (c) { |
2116 | 0 | if (c->u.node.addr == (addr & c->u.node.mask)) |
2117 | 0 | break; |
2118 | 0 | c = c->next; |
2119 | 0 | } |
2120 | 0 | break; |
2121 | 0 | } |
2122 | | |
2123 | 0 | case AF_INET6: |
2124 | 0 | if (addrlen != sizeof(uint64_t) * 2) { |
2125 | 0 | rc = -EINVAL; |
2126 | 0 | goto out; |
2127 | 0 | } |
2128 | | |
2129 | 0 | c = policydb->ocontexts[OCON_NODE6]; |
2130 | 0 | while (c) { |
2131 | 0 | if (match_ipv6_addrmask(addrp, c->u.node6.addr, |
2132 | 0 | c->u.node6.mask)) |
2133 | 0 | break; |
2134 | 0 | c = c->next; |
2135 | 0 | } |
2136 | 0 | break; |
2137 | | |
2138 | 0 | default: |
2139 | 0 | *out_sid = SECINITSID_NODE; |
2140 | 0 | goto out; |
2141 | 0 | } |
2142 | | |
2143 | 0 | if (c) { |
2144 | 0 | if (!c->sid[0]) { |
2145 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
2146 | 0 | &c->sid[0]); |
2147 | 0 | if (rc) |
2148 | 0 | goto out; |
2149 | 0 | } |
2150 | 0 | *out_sid = c->sid[0]; |
2151 | 0 | } else { |
2152 | 0 | *out_sid = SECINITSID_NODE; |
2153 | 0 | } |
2154 | | |
2155 | 0 | out: |
2156 | 0 | return rc; |
2157 | 0 | } |
2158 | | |
2159 | | /* |
2160 | | * Generate the set of SIDs for legal security contexts |
2161 | | * for a given user that can be reached by `fromsid'. |
2162 | | * Set `*sids' to point to a dynamically allocated |
2163 | | * array containing the set of SIDs. Set `*nel' to the |
2164 | | * number of elements in the array. |
2165 | | */ |
2166 | 0 | #define SIDS_NEL 25 |
2167 | | |
2168 | | int sepol_get_user_sids(sepol_security_id_t fromsid, char *username, |
2169 | | sepol_security_id_t **sids, uint32_t *nel) |
2170 | 0 | { |
2171 | 0 | context_struct_t *fromcon, usercon; |
2172 | 0 | sepol_security_id_t *mysids, *mysids2, sid; |
2173 | 0 | uint32_t mynel = 0, maxnel = SIDS_NEL; |
2174 | 0 | user_datum_t *user; |
2175 | 0 | role_datum_t *role; |
2176 | 0 | struct sepol_av_decision avd; |
2177 | 0 | int rc = 0; |
2178 | 0 | unsigned int i, j, reason; |
2179 | 0 | ebitmap_node_t *rnode, *tnode; |
2180 | |
|
2181 | 0 | fromcon = sepol_sidtab_search(sidtab, fromsid); |
2182 | 0 | if (!fromcon) { |
2183 | 0 | rc = -EINVAL; |
2184 | 0 | goto out; |
2185 | 0 | } |
2186 | | |
2187 | 0 | user = (user_datum_t *)hashtab_search(policydb->p_users.table, |
2188 | 0 | username); |
2189 | 0 | if (!user) { |
2190 | 0 | rc = -EINVAL; |
2191 | 0 | goto out; |
2192 | 0 | } |
2193 | 0 | usercon.user = user->s.value; |
2194 | |
|
2195 | 0 | mysids = calloc(maxnel, sizeof(sepol_security_id_t)); |
2196 | 0 | if (!mysids) { |
2197 | 0 | rc = -ENOMEM; |
2198 | 0 | goto out; |
2199 | 0 | } |
2200 | | |
2201 | 0 | ebitmap_for_each_positive_bit(&user->roles.roles, rnode, i) { |
2202 | 0 | role = policydb->role_val_to_struct[i]; |
2203 | 0 | usercon.role = i + 1; |
2204 | 0 | ebitmap_for_each_positive_bit(&role->types.types, tnode, j) { |
2205 | 0 | usercon.type = j + 1; |
2206 | 0 | if (usercon.type == fromcon->type) |
2207 | 0 | continue; |
2208 | | |
2209 | 0 | if (mls_setup_user_range(fromcon, user, &usercon, |
2210 | 0 | policydb->mls)) |
2211 | 0 | continue; |
2212 | | |
2213 | 0 | rc = context_struct_compute_av(fromcon, &usercon, |
2214 | 0 | policydb->process_class, |
2215 | 0 | policydb->process_trans, |
2216 | 0 | &avd, &reason, NULL, 0); |
2217 | 0 | if (rc || !(avd.allowed & policydb->process_trans)) |
2218 | 0 | continue; |
2219 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &usercon, |
2220 | 0 | &sid); |
2221 | 0 | if (rc) { |
2222 | 0 | free(mysids); |
2223 | 0 | goto out; |
2224 | 0 | } |
2225 | 0 | if (mynel < maxnel) { |
2226 | 0 | mysids[mynel++] = sid; |
2227 | 0 | } else { |
2228 | 0 | maxnel += SIDS_NEL; |
2229 | 0 | mysids2 = calloc(maxnel, |
2230 | 0 | sizeof(sepol_security_id_t)); |
2231 | 0 | if (!mysids2) { |
2232 | 0 | rc = -ENOMEM; |
2233 | 0 | free(mysids); |
2234 | 0 | goto out; |
2235 | 0 | } |
2236 | 0 | memcpy(mysids2, mysids, |
2237 | 0 | mynel * sizeof(sepol_security_id_t)); |
2238 | 0 | free(mysids); |
2239 | 0 | mysids = mysids2; |
2240 | 0 | mysids[mynel++] = sid; |
2241 | 0 | } |
2242 | 0 | } |
2243 | 0 | } |
2244 | | |
2245 | 0 | *sids = mysids; |
2246 | 0 | *nel = mynel; |
2247 | |
|
2248 | 0 | out: |
2249 | 0 | return rc; |
2250 | 0 | } |
2251 | | |
2252 | | /* |
2253 | | * Return the SID to use for a file in a filesystem |
2254 | | * that cannot support a persistent label mapping or use another |
2255 | | * fixed labeling behavior like transition SIDs or task SIDs. |
2256 | | */ |
2257 | | int sepol_genfs_sid(const char *fstype, const char *path, |
2258 | | sepol_security_class_t sclass, sepol_security_id_t *sid) |
2259 | 0 | { |
2260 | 0 | size_t len; |
2261 | 0 | genfs_t *genfs; |
2262 | 0 | ocontext_t *c; |
2263 | 0 | int rc = 0, cmp = 0; |
2264 | |
|
2265 | 0 | for (genfs = policydb->genfs; genfs; genfs = genfs->next) { |
2266 | 0 | cmp = strcmp(fstype, genfs->fstype); |
2267 | 0 | if (cmp <= 0) |
2268 | 0 | break; |
2269 | 0 | } |
2270 | |
|
2271 | 0 | if (!genfs || cmp) { |
2272 | 0 | *sid = SECINITSID_UNLABELED; |
2273 | 0 | rc = -ENOENT; |
2274 | 0 | goto out; |
2275 | 0 | } |
2276 | | |
2277 | 0 | for (c = genfs->head; c; c = c->next) { |
2278 | 0 | len = strlen(c->u.name); |
2279 | 0 | if ((!c->v.sclass || sclass == c->v.sclass) && |
2280 | 0 | (strncmp(c->u.name, path, len) == 0)) |
2281 | 0 | break; |
2282 | 0 | } |
2283 | |
|
2284 | 0 | if (!c) { |
2285 | 0 | *sid = SECINITSID_UNLABELED; |
2286 | 0 | rc = -ENOENT; |
2287 | 0 | goto out; |
2288 | 0 | } |
2289 | | |
2290 | 0 | if (!c->sid[0]) { |
2291 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
2292 | 0 | &c->sid[0]); |
2293 | 0 | if (rc) |
2294 | 0 | goto out; |
2295 | 0 | } |
2296 | | |
2297 | 0 | *sid = c->sid[0]; |
2298 | 0 | out: |
2299 | 0 | return rc; |
2300 | 0 | } |
2301 | | |
2302 | | int sepol_fs_use(const char *fstype, unsigned int *behavior, |
2303 | | sepol_security_id_t *sid) |
2304 | 0 | { |
2305 | 0 | int rc = 0; |
2306 | 0 | ocontext_t *c; |
2307 | |
|
2308 | 0 | c = policydb->ocontexts[OCON_FSUSE]; |
2309 | 0 | while (c) { |
2310 | 0 | if (strcmp(fstype, c->u.name) == 0) |
2311 | 0 | break; |
2312 | 0 | c = c->next; |
2313 | 0 | } |
2314 | |
|
2315 | 0 | if (c) { |
2316 | 0 | *behavior = c->v.behavior; |
2317 | 0 | if (!c->sid[0]) { |
2318 | 0 | rc = sepol_sidtab_context_to_sid(sidtab, &c->context[0], |
2319 | 0 | &c->sid[0]); |
2320 | 0 | if (rc) |
2321 | 0 | goto out; |
2322 | 0 | } |
2323 | 0 | *sid = c->sid[0]; |
2324 | 0 | } else { |
2325 | 0 | rc = sepol_genfs_sid(fstype, "/", policydb->dir_class, sid); |
2326 | 0 | if (rc) { |
2327 | 0 | *behavior = SECURITY_FS_USE_NONE; |
2328 | 0 | rc = 0; |
2329 | 0 | } else { |
2330 | 0 | *behavior = SECURITY_FS_USE_GENFS; |
2331 | 0 | } |
2332 | 0 | } |
2333 | | |
2334 | 0 | out: |
2335 | 0 | return rc; |
2336 | 0 | } |
2337 | | |
2338 | | /* FLASK */ |