aboutsummaryrefslogtreecommitdiffstats
path: root/security/apparmor/domain.c
diff options
context:
space:
mode:
authorJohn Johansen <john.johansen@canonical.com>2010-07-29 17:48:06 -0400
committerJames Morris <jmorris@namei.org>2010-08-02 01:35:14 -0400
commit898127c34ec03291c86f4ff3856d79e9e18952bc (patch)
treec8845bd204b1c4b120f1be1cceea4ff96f749e53 /security/apparmor/domain.c
parent6380bd8ddf613b29f478396308b591867d401de4 (diff)
AppArmor: functions for domain transitions
AppArmor routines for controling domain transitions, which can occur at exec or through self directed change_profile/change_hat calls. Unconfined tasks are checked at exec against the profiles in the confining profile namespace to determine if a profile should be attached to the task. Confined tasks execs are controlled by the profile which provides rules determining which execs are allowed and if so which profiles should be transitioned to. Self directed domain transitions allow a task to request transition to a given profile. If the transition is allowed then the profile will be applied, either immeditately or at exec time depending on the request. Immeditate self directed transitions have several security limitations but have uses in setting up stub transition profiles and other limited cases. Signed-off-by: John Johansen <john.johansen@canonical.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/apparmor/domain.c')
-rw-r--r--security/apparmor/domain.c823
1 files changed, 823 insertions, 0 deletions
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
new file mode 100644
index 000000000000..08bbe6397a7f
--- /dev/null
+++ b/security/apparmor/domain.c
@@ -0,0 +1,823 @@
1/*
2 * AppArmor security module
3 *
4 * This file contains AppArmor policy attachment and domain transitions
5 *
6 * Copyright (C) 2002-2008 Novell/SUSE
7 * Copyright 2009-2010 Canonical Ltd.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation, version 2 of the
12 * License.
13 */
14
15#include <linux/errno.h>
16#include <linux/fdtable.h>
17#include <linux/file.h>
18#include <linux/mount.h>
19#include <linux/syscalls.h>
20#include <linux/tracehook.h>
21#include <linux/personality.h>
22
23#include "include/audit.h"
24#include "include/apparmorfs.h"
25#include "include/context.h"
26#include "include/domain.h"
27#include "include/file.h"
28#include "include/ipc.h"
29#include "include/match.h"
30#include "include/path.h"
31#include "include/policy.h"
32
33/**
34 * aa_free_domain_entries - free entries in a domain table
35 * @domain: the domain table to free (MAYBE NULL)
36 */
37void aa_free_domain_entries(struct aa_domain *domain)
38{
39 int i;
40 if (domain) {
41 if (!domain->table)
42 return;
43
44 for (i = 0; i < domain->size; i++)
45 kzfree(domain->table[i]);
46 kzfree(domain->table);
47 domain->table = NULL;
48 }
49}
50
51/**
52 * may_change_ptraced_domain - check if can change profile on ptraced task
53 * @task: task we want to change profile of (NOT NULL)
54 * @to_profile: profile to change to (NOT NULL)
55 *
56 * Check if the task is ptraced and if so if the tracing task is allowed
57 * to trace the new domain
58 *
59 * Returns: %0 or error if change not allowed
60 */
61static int may_change_ptraced_domain(struct task_struct *task,
62 struct aa_profile *to_profile)
63{
64 struct task_struct *tracer;
65 struct cred *cred = NULL;
66 struct aa_profile *tracerp = NULL;
67 int error = 0;
68
69 rcu_read_lock();
70 tracer = tracehook_tracer_task(task);
71 if (tracer) {
72 /* released below */
73 cred = get_task_cred(tracer);
74 tracerp = aa_cred_profile(cred);
75 }
76 rcu_read_unlock();
77
78 /* not ptraced */
79 if (!tracer || unconfined(tracerp))
80 goto out;
81
82 error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
83
84out:
85 if (cred)
86 put_cred(cred);
87
88 return error;
89}
90
91/**
92 * change_profile_perms - find permissions for change_profile
93 * @profile: the current profile (NOT NULL)
94 * @ns: the namespace being switched to (NOT NULL)
95 * @name: the name of the profile to change to (NOT NULL)
96 * @request: requested perms
97 * @start: state to start matching in
98 *
99 * Returns: permission set
100 */
101static struct file_perms change_profile_perms(struct aa_profile *profile,
102 struct aa_namespace *ns,
103 const char *name, u32 request,
104 unsigned int start)
105{
106 struct file_perms perms;
107 struct path_cond cond = { };
108 unsigned int state;
109
110 if (unconfined(profile)) {
111 perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
112 perms.audit = perms.quiet = perms.kill = 0;
113 return perms;
114 } else if (!profile->file.dfa) {
115 return nullperms;
116 } else if ((ns == profile->ns)) {
117 /* try matching against rules with out namespace prepended */
118 aa_str_perms(profile->file.dfa, start, name, &cond, &perms);
119 if (COMBINED_PERM_MASK(perms) & request)
120 return perms;
121 }
122
123 /* try matching with namespace name and then profile */
124 state = aa_dfa_match(profile->file.dfa, start, ns->base.name);
125 state = aa_dfa_match_len(profile->file.dfa, state, ":", 1);
126 aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
127
128 return perms;
129}
130
131/**
132 * __attach_match_ - find an attachment match
133 * @name - to match against (NOT NULL)
134 * @head - profile list to walk (NOT NULL)
135 *
136 * Do a linear search on the profiles in the list. There is a matching
137 * preference where an exact match is preferred over a name which uses
138 * expressions to match, and matching expressions with the greatest
139 * xmatch_len are preferred.
140 *
141 * Requires: @head not be shared or have appropriate locks held
142 *
143 * Returns: profile or NULL if no match found
144 */
145static struct aa_profile *__attach_match(const char *name,
146 struct list_head *head)
147{
148 int len = 0;
149 struct aa_profile *profile, *candidate = NULL;
150
151 list_for_each_entry(profile, head, base.list) {
152 if (profile->flags & PFLAG_NULL)
153 continue;
154 if (profile->xmatch && profile->xmatch_len > len) {
155 unsigned int state = aa_dfa_match(profile->xmatch,
156 DFA_START, name);
157 u32 perm = dfa_user_allow(profile->xmatch, state);
158 /* any accepting state means a valid match. */
159 if (perm & MAY_EXEC) {
160 candidate = profile;
161 len = profile->xmatch_len;
162 }
163 } else if (!strcmp(profile->base.name, name))
164 /* exact non-re match, no more searching required */
165 return profile;
166 }
167
168 return candidate;
169}
170
171/**
172 * find_attach - do attachment search for unconfined processes
173 * @ns: the current namespace (NOT NULL)
174 * @list: list to search (NOT NULL)
175 * @name: the executable name to match against (NOT NULL)
176 *
177 * Returns: profile or NULL if no match found
178 */
179static struct aa_profile *find_attach(struct aa_namespace *ns,
180 struct list_head *list, const char *name)
181{
182 struct aa_profile *profile;
183
184 read_lock(&ns->lock);
185 profile = aa_get_profile(__attach_match(name, list));
186 read_unlock(&ns->lock);
187
188 return profile;
189}
190
191/**
192 * separate_fqname - separate the namespace and profile names
193 * @fqname: the fqname name to split (NOT NULL)
194 * @ns_name: the namespace name if it exists (NOT NULL)
195 *
196 * This is the xtable equivalent routine of aa_split_fqname. It finds the
197 * split in an xtable fqname which contains an embedded \0 instead of a :
198 * if a namespace is specified. This is done so the xtable is constant and
199 * isn't re-split on every lookup.
200 *
201 * Either the profile or namespace name may be optional but if the namespace
202 * is specified the profile name termination must be present. This results
203 * in the following possible encodings:
204 * profile_name\0
205 * :ns_name\0profile_name\0
206 * :ns_name\0\0
207 *
208 * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table
209 *
210 * Returns: profile name if it is specified else NULL
211 */
212static const char *separate_fqname(const char *fqname, const char **ns_name)
213{
214 const char *name;
215
216 if (fqname[0] == ':') {
217 /* In this case there is guaranteed to be two \0 terminators
218 * in the string. They are verified at load time by
219 * by unpack_trans_table
220 */
221 *ns_name = fqname + 1; /* skip : */
222 name = *ns_name + strlen(*ns_name) + 1;
223 if (!*name)
224 name = NULL;
225 } else {
226 *ns_name = NULL;
227 name = fqname;
228 }
229
230 return name;
231}
232
233static const char *next_name(int xtype, const char *name)
234{
235 return NULL;
236}
237
238/**
239 * x_table_lookup - lookup an x transition name via transition table
240 * @profile: current profile (NOT NULL)
241 * @xindex: index into x transition table
242 *
243 * Returns: refcounted profile, or NULL on failure (MAYBE NULL)
244 */
245static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
246{
247 struct aa_profile *new_profile = NULL;
248 struct aa_namespace *ns = profile->ns;
249 u32 xtype = xindex & AA_X_TYPE_MASK;
250 int index = xindex & AA_X_INDEX_MASK;
251 const char *name;
252
253 /* index is guaranteed to be in range, validated at load time */
254 for (name = profile->file.trans.table[index]; !new_profile && name;
255 name = next_name(xtype, name)) {
256 struct aa_namespace *new_ns;
257 const char *xname = NULL;
258
259 new_ns = NULL;
260 if (xindex & AA_X_CHILD) {
261 /* release by caller */
262 new_profile = aa_find_child(profile, name);
263 continue;
264 } else if (*name == ':') {
265 /* switching namespace */
266 const char *ns_name;
267 xname = name = separate_fqname(name, &ns_name);
268 if (!xname)
269 /* no name so use profile name */
270 xname = profile->base.hname;
271 if (*ns_name == '@') {
272 /* TODO: variable support */
273 ;
274 }
275 /* released below */
276 new_ns = aa_find_namespace(ns, ns_name);
277 if (!new_ns)
278 continue;
279 } else if (*name == '@') {
280 /* TODO: variable support */
281 continue;
282 } else {
283 /* basic namespace lookup */
284 xname = name;
285 }
286
287 /* released by caller */
288 new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname);
289 aa_put_namespace(new_ns);
290 }
291
292 /* released by caller */
293 return new_profile;
294}
295
296/**
297 * x_to_profile - get target profile for a given xindex
298 * @profile: current profile (NOT NULL)
299 * @name: name to lookup (NOT NULL)
300 * @xindex: index into x transition table
301 *
302 * find profile for a transition index
303 *
304 * Returns: refcounted profile or NULL if not found available
305 */
306static struct aa_profile *x_to_profile(struct aa_profile *profile,
307 const char *name, u32 xindex)
308{
309 struct aa_profile *new_profile = NULL;
310 struct aa_namespace *ns = profile->ns;
311 u32 xtype = xindex & AA_X_TYPE_MASK;
312
313 switch (xtype) {
314 case AA_X_NONE:
315 /* fail exec unless ix || ux fallback - handled by caller */
316 return NULL;
317 case AA_X_NAME:
318 if (xindex & AA_X_CHILD)
319 /* released by caller */
320 new_profile = find_attach(ns, &profile->base.profiles,
321 name);
322 else
323 /* released by caller */
324 new_profile = find_attach(ns, &ns->base.profiles,
325 name);
326 break;
327 case AA_X_TABLE:
328 /* released by caller */
329 new_profile = x_table_lookup(profile, xindex);
330 break;
331 }
332
333 /* released by caller */
334 return new_profile;
335}
336
337/**
338 * apparmor_bprm_set_creds - set the new creds on the bprm struct
339 * @bprm: binprm for the exec (NOT NULL)
340 *
341 * Returns: %0 or error on failure
342 */
343int apparmor_bprm_set_creds(struct linux_binprm *bprm)
344{
345 struct aa_task_cxt *cxt;
346 struct aa_profile *profile, *new_profile = NULL;
347 struct aa_namespace *ns;
348 char *buffer = NULL;
349 unsigned int state;
350 struct file_perms perms = {};
351 struct path_cond cond = {
352 bprm->file->f_path.dentry->d_inode->i_uid,
353 bprm->file->f_path.dentry->d_inode->i_mode
354 };
355 const char *name = NULL, *target = NULL, *info = NULL;
356 int error = cap_bprm_set_creds(bprm);
357 if (error)
358 return error;
359
360 if (bprm->cred_prepared)
361 return 0;
362
363 cxt = bprm->cred->security;
364 BUG_ON(!cxt);
365
366 profile = aa_get_profile(aa_newest_version(cxt->profile));
367 /*
368 * get the namespace from the replacement profile as replacement
369 * can change the namespace
370 */
371 ns = profile->ns;
372 state = profile->file.start;
373
374 /* buffer freed below, name is pointer into buffer */
375 error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer,
376 &name);
377 if (error) {
378 if (profile->flags &
379 (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED))
380 error = 0;
381 info = "Exec failed name resolution";
382 name = bprm->filename;
383 goto audit;
384 }
385
386 /* Test for onexec first as onexec directives override other
387 * x transitions.
388 */
389 if (unconfined(profile)) {
390 /* unconfined task */
391 if (cxt->onexec)
392 /* change_profile on exec already been granted */
393 new_profile = aa_get_profile(cxt->onexec);
394 else
395 new_profile = find_attach(ns, &ns->base.profiles, name);
396 if (!new_profile)
397 goto cleanup;
398 goto apply;
399 }
400
401 /* find exec permissions for name */
402 state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms);
403 if (cxt->onexec) {
404 struct file_perms cp;
405 info = "change_profile onexec";
406 if (!(perms.allow & AA_MAY_ONEXEC))
407 goto audit;
408
409 /* test if this exec can be paired with change_profile onexec.
410 * onexec permission is linked to exec with a standard pairing
411 * exec\0change_profile
412 */
413 state = aa_dfa_null_transition(profile->file.dfa, state);
414 cp = change_profile_perms(profile, cxt->onexec->ns, name,
415 AA_MAY_ONEXEC, state);
416
417 if (!(cp.allow & AA_MAY_ONEXEC))
418 goto audit;
419 new_profile = aa_get_profile(aa_newest_version(cxt->onexec));
420 goto apply;
421 }
422
423 if (perms.allow & MAY_EXEC) {
424 /* exec permission determine how to transition */
425 new_profile = x_to_profile(profile, name, perms.xindex);
426 if (!new_profile) {
427 if (perms.xindex & AA_X_INHERIT) {
428 /* (p|c|n)ix - don't change profile but do
429 * use the newest version, which was picked
430 * up above when getting profile
431 */
432 info = "ix fallback";
433 new_profile = aa_get_profile(profile);
434 goto x_clear;
435 } else if (perms.xindex & AA_X_UNCONFINED) {
436 new_profile = aa_get_profile(ns->unconfined);
437 info = "ux fallback";
438 } else {
439 error = -ENOENT;
440 info = "profile not found";
441 }
442 }
443 } else if (COMPLAIN_MODE(profile)) {
444 /* no exec permission - are we in learning mode */
445 new_profile = aa_new_null_profile(profile, 0);
446 if (!new_profile) {
447 error = -ENOMEM;
448 info = "could not create null profile";
449 } else {
450 error = -EACCES;
451 target = new_profile->base.hname;
452 }
453 perms.xindex |= AA_X_UNSAFE;
454 } else
455 /* fail exec */
456 error = -EACCES;
457
458 if (!new_profile)
459 goto audit;
460
461 if (bprm->unsafe & LSM_UNSAFE_SHARE) {
462 /* FIXME: currently don't mediate shared state */
463 ;
464 }
465
466 if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
467 error = may_change_ptraced_domain(current, new_profile);
468 if (error) {
469 aa_put_profile(new_profile);
470 goto audit;
471 }
472 }
473
474 /* Determine if secure exec is needed.
475 * Can be at this point for the following reasons:
476 * 1. unconfined switching to confined
477 * 2. confined switching to different confinement
478 * 3. confined switching to unconfined
479 *
480 * Cases 2 and 3 are marked as requiring secure exec
481 * (unless policy specified "unsafe exec")
482 *
483 * bprm->unsafe is used to cache the AA_X_UNSAFE permission
484 * to avoid having to recompute in secureexec
485 */
486 if (!(perms.xindex & AA_X_UNSAFE)) {
487 AA_DEBUG("scrubbing environment variables for %s profile=%s\n",
488 name, new_profile->base.hname);
489 bprm->unsafe |= AA_SECURE_X_NEEDED;
490 }
491apply:
492 target = new_profile->base.hname;
493 /* when transitioning profiles clear unsafe personality bits */
494 bprm->per_clear |= PER_CLEAR_ON_SETID;
495
496x_clear:
497 aa_put_profile(cxt->profile);
498 /* transfer new profile reference will be released when cxt is freed */
499 cxt->profile = new_profile;
500
501 /* clear out all temporary/transitional state from the context */
502 aa_put_profile(cxt->previous);
503 aa_put_profile(cxt->onexec);
504 cxt->previous = NULL;
505 cxt->onexec = NULL;
506 cxt->token = 0;
507
508audit:
509 error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
510 name, target, cond.uid, info, error);
511
512cleanup:
513 aa_put_profile(profile);
514 kfree(buffer);
515
516 return error;
517}
518
519/**
520 * apparmor_bprm_secureexec - determine if secureexec is needed
521 * @bprm: binprm for exec (NOT NULL)
522 *
523 * Returns: %1 if secureexec is needed else %0
524 */
525int apparmor_bprm_secureexec(struct linux_binprm *bprm)
526{
527 int ret = cap_bprm_secureexec(bprm);
528
529 /* the decision to use secure exec is computed in set_creds
530 * and stored in bprm->unsafe.
531 */
532 if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
533 ret = 1;
534
535 return ret;
536}
537
538/**
539 * apparmor_bprm_committing_creds - do task cleanup on committing new creds
540 * @bprm: binprm for the exec (NOT NULL)
541 */
542void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
543{
544 struct aa_profile *profile = __aa_current_profile();
545 struct aa_task_cxt *new_cxt = bprm->cred->security;
546
547 /* bail out if unconfined or not changing profile */
548 if ((new_cxt->profile == profile) ||
549 (unconfined(new_cxt->profile)))
550 return;
551
552 current->pdeath_signal = 0;
553
554 /* reset soft limits and set hard limits for the new profile */
555 __aa_transition_rlimits(profile, new_cxt->profile);
556}
557
558/**
559 * apparmor_bprm_commited_cred - do cleanup after new creds committed
560 * @bprm: binprm for the exec (NOT NULL)
561 */
562void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
563{
564 /* TODO: cleanup signals - ipc mediation */
565 return;
566}
567
568/*
569 * Functions for self directed profile change
570 */
571
572/**
573 * new_compound_name - create an hname with @n2 appended to @n1
574 * @n1: base of hname (NOT NULL)
575 * @n2: name to append (NOT NULL)
576 *
577 * Returns: new name or NULL on error
578 */
579static char *new_compound_name(const char *n1, const char *n2)
580{
581 char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL);
582 if (name)
583 sprintf(name, "%s//%s", n1, n2);
584 return name;
585}
586
587/**
588 * aa_change_hat - change hat to/from subprofile
589 * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0)
590 * @count: number of hat names in @hats
591 * @token: magic value to validate the hat change
592 * @permtest: true if this is just a permission test
593 *
594 * Change to the first profile specified in @hats that exists, and store
595 * the @hat_magic in the current task context. If the count == 0 and the
596 * @token matches that stored in the current task context, return to the
597 * top level profile.
598 *
599 * Returns %0 on success, error otherwise.
600 */
601int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
602{
603 const struct cred *cred;
604 struct aa_task_cxt *cxt;
605 struct aa_profile *profile, *previous_profile, *hat = NULL;
606 char *name = NULL;
607 int i;
608 struct file_perms perms = {};
609 const char *target = NULL, *info = NULL;
610 int error = 0;
611
612 /* released below */
613 cred = get_current_cred();
614 cxt = cred->security;
615 profile = aa_cred_profile(cred);
616 previous_profile = cxt->previous;
617
618 if (unconfined(profile)) {
619 info = "unconfined";
620 error = -EPERM;
621 goto audit;
622 }
623
624 if (count) {
625 /* attempting to change into a new hat or switch to a sibling */
626 struct aa_profile *root;
627 root = PROFILE_IS_HAT(profile) ? profile->parent : profile;
628
629 /* find first matching hat */
630 for (i = 0; i < count && !hat; i++)
631 /* released below */
632 hat = aa_find_child(root, hats[i]);
633 if (!hat) {
634 if (!COMPLAIN_MODE(root) || permtest) {
635 if (list_empty(&root->base.profiles))
636 error = -ECHILD;
637 else
638 error = -ENOENT;
639 goto out;
640 }
641
642 /*
643 * In complain mode and failed to match any hats.
644 * Audit the failure is based off of the first hat
645 * supplied. This is done due how userspace
646 * interacts with change_hat.
647 *
648 * TODO: Add logging of all failed hats
649 */
650
651 /* freed below */
652 name = new_compound_name(root->base.hname, hats[0]);
653 target = name;
654 /* released below */
655 hat = aa_new_null_profile(profile, 1);
656 if (!hat) {
657 info = "failed null profile create";
658 error = -ENOMEM;
659 goto audit;
660 }
661 } else {
662 target = hat->base.hname;
663 if (!PROFILE_IS_HAT(hat)) {
664 info = "target not hat";
665 error = -EPERM;
666 goto audit;
667 }
668 }
669
670 error = may_change_ptraced_domain(current, hat);
671 if (error) {
672 info = "ptraced";
673 error = -EPERM;
674 goto audit;
675 }
676
677 if (!permtest) {
678 error = aa_set_current_hat(hat, token);
679 if (error == -EACCES)
680 /* kill task in case of brute force attacks */
681 perms.kill = AA_MAY_CHANGEHAT;
682 else if (name && !error)
683 /* reset error for learning of new hats */
684 error = -ENOENT;
685 }
686 } else if (previous_profile) {
687 /* Return to saved profile. Kill task if restore fails
688 * to avoid brute force attacks
689 */
690 target = previous_profile->base.hname;
691 error = aa_restore_previous_profile(token);
692 perms.kill = AA_MAY_CHANGEHAT;
693 } else
694 /* ignore restores when there is no saved profile */
695 goto out;
696
697audit:
698 if (!permtest)
699 error = aa_audit_file(profile, &perms, GFP_KERNEL,
700 OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
701 target, 0, info, error);
702
703out:
704 aa_put_profile(hat);
705 kfree(name);
706 put_cred(cred);
707
708 return error;
709}
710
711/**
712 * aa_change_profile - perform a one-way profile transition
713 * @ns_name: name of the profile namespace to change to (MAYBE NULL)
714 * @hname: name of profile to change to (MAYBE NULL)
715 * @onexec: whether this transition is to take place immediately or at exec
716 * @permtest: true if this is just a permission test
717 *
718 * Change to new profile @name. Unlike with hats, there is no way
719 * to change back. If @name isn't specified the current profile name is
720 * used.
721 * If @onexec then the transition is delayed until
722 * the next exec.
723 *
724 * Returns %0 on success, error otherwise.
725 */
726int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
727 bool permtest)
728{
729 const struct cred *cred;
730 struct aa_task_cxt *cxt;
731 struct aa_profile *profile, *target = NULL;
732 struct aa_namespace *ns = NULL;
733 struct file_perms perms = {};
734 const char *name = NULL, *info = NULL;
735 int op, error = 0;
736 u32 request;
737
738 if (!hname && !ns_name)
739 return -EINVAL;
740
741 if (onexec) {
742 request = AA_MAY_ONEXEC;
743 op = OP_CHANGE_ONEXEC;
744 } else {
745 request = AA_MAY_CHANGE_PROFILE;
746 op = OP_CHANGE_PROFILE;
747 }
748
749 cred = get_current_cred();
750 cxt = cred->security;
751 profile = aa_cred_profile(cred);
752
753 if (ns_name) {
754 /* released below */
755 ns = aa_find_namespace(profile->ns, ns_name);
756 if (!ns) {
757 /* we don't create new namespace in complain mode */
758 name = ns_name;
759 info = "namespace not found";
760 error = -ENOENT;
761 goto audit;
762 }
763 } else
764 /* released below */
765 ns = aa_get_namespace(profile->ns);
766
767 /* if the name was not specified, use the name of the current profile */
768 if (!hname) {
769 if (unconfined(profile))
770 hname = ns->unconfined->base.hname;
771 else
772 hname = profile->base.hname;
773 }
774
775 perms = change_profile_perms(profile, ns, hname, request,
776 profile->file.start);
777 if (!(perms.allow & request)) {
778 error = -EACCES;
779 goto audit;
780 }
781
782 /* released below */
783 target = aa_lookup_profile(ns, hname);
784 if (!target) {
785 info = "profile not found";
786 error = -ENOENT;
787 if (permtest || !COMPLAIN_MODE(profile))
788 goto audit;
789 /* released below */
790 target = aa_new_null_profile(profile, 0);
791 if (!target) {
792 info = "failed null profile create";
793 error = -ENOMEM;
794 goto audit;
795 }
796 }
797
798 /* check if tracing task is allowed to trace target domain */
799 error = may_change_ptraced_domain(current, target);
800 if (error) {
801 info = "ptrace prevents transition";
802 goto audit;
803 }
804
805 if (permtest)
806 goto audit;
807
808 if (onexec)
809 error = aa_set_current_onexec(target);
810 else
811 error = aa_replace_current_profile(target);
812
813audit:
814 if (!permtest)
815 error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
816 name, hname, 0, info, error);
817
818 aa_put_namespace(ns);
819 aa_put_profile(target);
820 put_cred(cred);
821
822 return error;
823}