Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[firefly-linux-kernel-4.4.55.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_get_task_profile - Get another task's profile
73  * @task: task to query  (NOT NULL)
74  *
75  * Returns: counted reference to @task's profile
76  */
77 struct aa_profile *aa_get_task_profile(struct task_struct *task)
78 {
79         struct aa_profile *p;
80
81         rcu_read_lock();
82         p = aa_get_profile(__aa_task_profile(task));
83         rcu_read_unlock();
84
85         return p;
86 }
87
88 /**
89  * aa_replace_current_profile - replace the current tasks profiles
90  * @profile: new profile  (NOT NULL)
91  *
92  * Returns: 0 or error on failure
93  */
94 int aa_replace_current_profile(struct aa_profile *profile)
95 {
96         struct aa_task_cxt *cxt = current_cxt();
97         struct cred *new;
98         BUG_ON(!profile);
99
100         if (cxt->profile == profile)
101                 return 0;
102
103         new  = prepare_creds();
104         if (!new)
105                 return -ENOMEM;
106
107         cxt = cred_cxt(new);
108         if (unconfined(profile) || (cxt->profile->ns != profile->ns))
109                 /* if switching to unconfined or a different profile namespace
110                  * clear out context state
111                  */
112                 aa_clear_task_cxt_trans(cxt);
113
114         /* be careful switching cxt->profile, when racing replacement it
115          * is possible that cxt->profile->replacedby is the reference keeping
116          * @profile valid, so make sure to get its reference before dropping
117          * the reference on cxt->profile */
118         aa_get_profile(profile);
119         aa_put_profile(cxt->profile);
120         cxt->profile = profile;
121
122         commit_creds(new);
123         return 0;
124 }
125
126 /**
127  * aa_set_current_onexec - set the tasks change_profile to happen onexec
128  * @profile: system profile to set at exec  (MAYBE NULL to clear value)
129  *
130  * Returns: 0 or error on failure
131  */
132 int aa_set_current_onexec(struct aa_profile *profile)
133 {
134         struct aa_task_cxt *cxt;
135         struct cred *new = prepare_creds();
136         if (!new)
137                 return -ENOMEM;
138
139         cxt = cred_cxt(new);
140         aa_get_profile(profile);
141         aa_put_profile(cxt->onexec);
142         cxt->onexec = profile;
143
144         commit_creds(new);
145         return 0;
146 }
147
148 /**
149  * aa_set_current_hat - set the current tasks hat
150  * @profile: profile to set as the current hat  (NOT NULL)
151  * @token: token value that must be specified to change from the hat
152  *
153  * Do switch of tasks hat.  If the task is currently in a hat
154  * validate the token to match.
155  *
156  * Returns: 0 or error on failure
157  */
158 int aa_set_current_hat(struct aa_profile *profile, u64 token)
159 {
160         struct aa_task_cxt *cxt;
161         struct cred *new = prepare_creds();
162         if (!new)
163                 return -ENOMEM;
164         BUG_ON(!profile);
165
166         cxt = cred_cxt(new);
167         if (!cxt->previous) {
168                 /* transfer refcount */
169                 cxt->previous = cxt->profile;
170                 cxt->token = token;
171         } else if (cxt->token == token) {
172                 aa_put_profile(cxt->profile);
173         } else {
174                 /* previous_profile && cxt->token != token */
175                 abort_creds(new);
176                 return -EACCES;
177         }
178         cxt->profile = aa_get_profile(aa_newest_version(profile));
179         /* clear exec on switching context */
180         aa_put_profile(cxt->onexec);
181         cxt->onexec = NULL;
182
183         commit_creds(new);
184         return 0;
185 }
186
187 /**
188  * aa_restore_previous_profile - exit from hat context restoring the profile
189  * @token: the token that must be matched to exit hat context
190  *
191  * Attempt to return out of a hat to the previous profile.  The token
192  * must match the stored token value.
193  *
194  * Returns: 0 or error of failure
195  */
196 int aa_restore_previous_profile(u64 token)
197 {
198         struct aa_task_cxt *cxt;
199         struct cred *new = prepare_creds();
200         if (!new)
201                 return -ENOMEM;
202
203         cxt = cred_cxt(new);
204         if (cxt->token != token) {
205                 abort_creds(new);
206                 return -EACCES;
207         }
208         /* ignore restores when there is no saved profile */
209         if (!cxt->previous) {
210                 abort_creds(new);
211                 return 0;
212         }
213
214         aa_put_profile(cxt->profile);
215         cxt->profile = aa_newest_version(cxt->previous);
216         BUG_ON(!cxt->profile);
217         if (unlikely(cxt->profile != cxt->previous)) {
218                 aa_get_profile(cxt->profile);
219                 aa_put_profile(cxt->previous);
220         }
221         /* ref has been transfered so avoid putting ref in clear_task_cxt */
222         cxt->previous = NULL;
223         /* clear exec && prev information when restoring to previous context */
224         aa_clear_task_cxt_trans(cxt);
225
226         commit_creds(new);
227         return 0;
228 }