Merge branch 'x86-build-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6.git] / security / apparmor / context.c
1 /*
2  * AppArmor security module
3  *
4  * This file contains AppArmor functions used to manipulate object security
5  * contexts.
6  *
7  * Copyright (C) 1998-2008 Novell/SUSE
8  * Copyright 2009-2010 Canonical Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation, version 2 of the
13  * License.
14  *
15  *
16  * AppArmor sets confinement on every task, via the the aa_task_cxt and
17  * the aa_task_cxt.profile, both of which are required and are not allowed
18  * to be NULL.  The aa_task_cxt is not reference counted and is unique
19  * to each cred (which is reference count).  The profile pointed to by
20  * the task_cxt is reference counted.
21  *
22  * TODO
23  * If a task uses change_hat it currently does not return to the old
24  * cred or task context but instead creates a new one.  Ideally the task
25  * should return to the previous cred if it has not been modified.
26  *
27  */
28
29 #include "include/context.h"
30 #include "include/policy.h"
31
32 /**
33  * aa_alloc_task_context - allocate a new task_cxt
34  * @flags: gfp flags for allocation
35  *
36  * Returns: allocated buffer or NULL on failure
37  */
38 struct aa_task_cxt *aa_alloc_task_context(gfp_t flags)
39 {
40         return kzalloc(sizeof(struct aa_task_cxt), flags);
41 }
42
43 /**
44  * aa_free_task_context - free a task_cxt
45  * @cxt: task_cxt to free (MAYBE NULL)
46  */
47 void aa_free_task_context(struct aa_task_cxt *cxt)
48 {
49         if (cxt) {
50                 aa_put_profile(cxt->profile);
51                 aa_put_profile(cxt->previous);
52                 aa_put_profile(cxt->onexec);
53
54                 kzfree(cxt);
55         }
56 }
57
58 /**
59  * aa_dup_task_context - duplicate a task context, incrementing reference counts
60  * @new: a blank task context      (NOT NULL)
61  * @old: the task context to copy  (NOT NULL)
62  */
63 void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old)
64 {
65         *new = *old;
66         aa_get_profile(new->profile);
67         aa_get_profile(new->previous);
68         aa_get_profile(new->onexec);
69 }
70
71 /**
72  * aa_replace_current_profile - replace the current tasks profiles
73  * @profile: new profile  (NOT NULL)
74  *
75  * Returns: 0 or error on failure
76  */
77 int aa_replace_current_profile(struct aa_profile *profile)
78 {
79         struct aa_task_cxt *cxt = current_cred()->security;
80         struct cred *new;
81         BUG_ON(!profile);
82
83         if (cxt->profile == profile)
84                 return 0;
85
86         new  = prepare_creds();
87         if (!new)
88                 return -ENOMEM;
89
90         cxt = new->security;
91         if (unconfined(profile) || (cxt->profile->ns != profile->ns)) {
92                 /* if switching to unconfined or a different profile namespace
93                  * clear out context state
94                  */
95                 aa_put_profile(cxt->previous);
96                 aa_put_profile(cxt->onexec);
97                 cxt->previous = NULL;
98                 cxt->onexec = NULL;
99                 cxt->token = 0;
100         }
101         /* be careful switching cxt->profile, when racing replacement it
102          * is possible that cxt->profile->replacedby is the reference keeping
103          * @profile valid, so make sure to get its reference before dropping
104          * the reference on cxt->profile */
105         aa_get_profile(profile);
106         aa_put_profile(cxt->profile);
107         cxt->profile = profile;
108
109         commit_creds(new);
110         return 0;
111 }
112
113 /**
114  * aa_set_current_onexec - set the tasks change_profile to happen onexec
115  * @profile: system profile to set at exec  (MAYBE NULL to clear value)
116  *
117  * Returns: 0 or error on failure
118  */
119 int aa_set_current_onexec(struct aa_profile *profile)
120 {
121         struct aa_task_cxt *cxt;
122         struct cred *new = prepare_creds();
123         if (!new)
124                 return -ENOMEM;
125
126         cxt = new->security;
127         aa_get_profile(profile);
128         aa_put_profile(cxt->onexec);
129         cxt->onexec = profile;
130
131         commit_creds(new);
132         return 0;
133 }
134
135 /**
136  * aa_set_current_hat - set the current tasks hat
137  * @profile: profile to set as the current hat  (NOT NULL)
138  * @token: token value that must be specified to change from the hat
139  *
140  * Do switch of tasks hat.  If the task is currently in a hat
141  * validate the token to match.
142  *
143  * Returns: 0 or error on failure
144  */
145 int aa_set_current_hat(struct aa_profile *profile, u64 token)
146 {
147         struct aa_task_cxt *cxt;
148         struct cred *new = prepare_creds();
149         if (!new)
150                 return -ENOMEM;
151         BUG_ON(!profile);
152
153         cxt = new->security;
154         if (!cxt->previous) {
155                 /* transfer refcount */
156                 cxt->previous = cxt->profile;
157                 cxt->token = token;
158         } else if (cxt->token == token) {
159                 aa_put_profile(cxt->profile);
160         } else {
161                 /* previous_profile && cxt->token != token */
162                 abort_creds(new);
163                 return -EACCES;
164         }
165         cxt->profile = aa_get_profile(aa_newest_version(profile));
166         /* clear exec on switching context */
167         aa_put_profile(cxt->onexec);
168         cxt->onexec = NULL;
169
170         commit_creds(new);
171         return 0;
172 }
173
174 /**
175  * aa_restore_previous_profile - exit from hat context restoring the profile
176  * @token: the token that must be matched to exit hat context
177  *
178  * Attempt to return out of a hat to the previous profile.  The token
179  * must match the stored token value.
180  *
181  * Returns: 0 or error of failure
182  */
183 int aa_restore_previous_profile(u64 token)
184 {
185         struct aa_task_cxt *cxt;
186         struct cred *new = prepare_creds();
187         if (!new)
188                 return -ENOMEM;
189
190         cxt = new->security;
191         if (cxt->token != token) {
192                 abort_creds(new);
193                 return -EACCES;
194         }
195         /* ignore restores when there is no saved profile */
196         if (!cxt->previous) {
197                 abort_creds(new);
198                 return 0;
199         }
200
201         aa_put_profile(cxt->profile);
202         cxt->profile = aa_newest_version(cxt->previous);
203         BUG_ON(!cxt->profile);
204         if (unlikely(cxt->profile != cxt->previous)) {
205                 aa_get_profile(cxt->profile);
206                 aa_put_profile(cxt->previous);
207         }
208         /* clear exec && prev information when restoring to previous context */
209         cxt->previous = NULL;
210         cxt->token = 0;
211         aa_put_profile(cxt->onexec);
212         cxt->onexec = NULL;
213
214         commit_creds(new);
215         return 0;
216 }