blob: 88b3b3c110e33d32b083a5afed10138ca0718c2d [file] [log] [blame]
John Johansencff281f2017-01-16 00:42:15 -08001/*
2 * AppArmor security module
3 *
4 * This file contains AppArmor policy manipulation functions
5 *
6 * Copyright (C) 1998-2008 Novell/SUSE
7 * Copyright 2009-2017 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 * AppArmor policy namespaces, allow for different sets of policies
15 * to be loaded for tasks within the namespace.
16 */
17
18#include <linux/list.h>
19#include <linux/mutex.h>
20#include <linux/slab.h>
21#include <linux/string.h>
22
23#include "include/apparmor.h"
24#include "include/context.h"
25#include "include/policy_ns.h"
26#include "include/policy.h"
27
28/* root profile namespace */
John Johansen98849df2017-01-16 00:42:16 -080029struct aa_ns *root_ns;
John Johansencff281f2017-01-16 00:42:15 -080030const char *aa_hidden_ns_name = "---";
31
32/**
33 * aa_ns_visible - test if @view is visible from @curr
34 * @curr: namespace to treat as the parent (NOT NULL)
35 * @view: namespace to test if visible from @curr (NOT NULL)
36 *
37 * Returns: true if @view is visible from @curr else false
38 */
John Johansen98849df2017-01-16 00:42:16 -080039bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view)
John Johansencff281f2017-01-16 00:42:15 -080040{
41 if (curr == view)
42 return true;
43
44 for ( ; view; view = view->parent) {
45 if (view->parent == curr)
46 return true;
47 }
48 return false;
49}
50
51/**
52 * aa_na_name - Find the ns name to display for @view from @curr
53 * @curr - current namespace (NOT NULL)
54 * @view - namespace attempting to view (NOT NULL)
55 *
56 * Returns: name of @view visible from @curr
57 */
John Johansen98849df2017-01-16 00:42:16 -080058const char *aa_ns_name(struct aa_ns *curr, struct aa_ns *view)
John Johansencff281f2017-01-16 00:42:15 -080059{
60 /* if view == curr then the namespace name isn't displayed */
61 if (curr == view)
62 return "";
63
64 if (aa_ns_visible(curr, view)) {
65 /* at this point if a ns is visible it is in a view ns
66 * thus the curr ns.hname is a prefix of its name.
67 * Only output the virtualized portion of the name
68 * Add + 2 to skip over // separating curr hname prefix
69 * from the visible tail of the views hname
70 */
71 return view->base.hname + strlen(curr->base.hname) + 2;
72 }
73
74 return aa_hidden_ns_name;
75}
76
77/**
John Johansen98849df2017-01-16 00:42:16 -080078 * alloc_ns - allocate, initialize and return a new namespace
John Johansencff281f2017-01-16 00:42:15 -080079 * @prefix: parent namespace name (MAYBE NULL)
80 * @name: a preallocated name (NOT NULL)
81 *
82 * Returns: refcounted namespace or NULL on failure.
83 */
John Johansen98849df2017-01-16 00:42:16 -080084static struct aa_ns *alloc_ns(const char *prefix, const char *name)
John Johansencff281f2017-01-16 00:42:15 -080085{
John Johansen98849df2017-01-16 00:42:16 -080086 struct aa_ns *ns;
John Johansencff281f2017-01-16 00:42:15 -080087
88 ns = kzalloc(sizeof(*ns), GFP_KERNEL);
89 AA_DEBUG("%s(%p)\n", __func__, ns);
90 if (!ns)
91 return NULL;
92 if (!aa_policy_init(&ns->base, prefix, name))
93 goto fail_ns;
94
95 INIT_LIST_HEAD(&ns->sub_ns);
96 mutex_init(&ns->lock);
97
John Johansen98849df2017-01-16 00:42:16 -080098 /* released by aa_free_ns() */
John Johansencff281f2017-01-16 00:42:15 -080099 ns->unconfined = aa_alloc_profile("unconfined");
100 if (!ns->unconfined)
101 goto fail_unconfined;
102
103 ns->unconfined->flags = PFLAG_IX_ON_NAME_ERROR |
104 PFLAG_IMMUTABLE | PFLAG_NS_COUNT;
105 ns->unconfined->mode = APPARMOR_UNCONFINED;
106
107 /* ns and ns->unconfined share ns->unconfined refcount */
108 ns->unconfined->ns = ns;
109
110 atomic_set(&ns->uniq_null, 0);
111
112 return ns;
113
114fail_unconfined:
115 kzfree(ns->base.hname);
116fail_ns:
117 kzfree(ns);
118 return NULL;
119}
120
121/**
John Johansen98849df2017-01-16 00:42:16 -0800122 * aa_free_ns - free a profile namespace
John Johansencff281f2017-01-16 00:42:15 -0800123 * @ns: the namespace to free (MAYBE NULL)
124 *
125 * Requires: All references to the namespace must have been put, if the
126 * namespace was referenced by a profile confining a task,
127 */
John Johansen98849df2017-01-16 00:42:16 -0800128void aa_free_ns(struct aa_ns *ns)
John Johansencff281f2017-01-16 00:42:15 -0800129{
130 if (!ns)
131 return;
132
133 aa_policy_destroy(&ns->base);
John Johansen98849df2017-01-16 00:42:16 -0800134 aa_put_ns(ns->parent);
John Johansencff281f2017-01-16 00:42:15 -0800135
136 ns->unconfined->ns = NULL;
137 aa_free_profile(ns->unconfined);
138 kzfree(ns);
139}
140
141/**
John Johansen98849df2017-01-16 00:42:16 -0800142 * aa_find_ns - look up a profile namespace on the namespace list
John Johansencff281f2017-01-16 00:42:15 -0800143 * @root: namespace to search in (NOT NULL)
144 * @name: name of namespace to find (NOT NULL)
145 *
146 * Returns: a refcounted namespace on the list, or NULL if no namespace
147 * called @name exists.
148 *
149 * refcount released by caller
150 */
John Johansen98849df2017-01-16 00:42:16 -0800151struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name)
John Johansencff281f2017-01-16 00:42:15 -0800152{
John Johansen98849df2017-01-16 00:42:16 -0800153 struct aa_ns *ns = NULL;
John Johansencff281f2017-01-16 00:42:15 -0800154
155 rcu_read_lock();
John Johansen98849df2017-01-16 00:42:16 -0800156 ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name));
John Johansencff281f2017-01-16 00:42:15 -0800157 rcu_read_unlock();
158
159 return ns;
160}
161
162/**
John Johansen98849df2017-01-16 00:42:16 -0800163 * aa_prepare_ns - find an existing or create a new namespace of @name
John Johansencff281f2017-01-16 00:42:15 -0800164 * @name: the namespace to find or add (MAYBE NULL)
165 *
John Johansen98849df2017-01-16 00:42:16 -0800166 * Returns: refcounted ns or NULL if failed to create one
John Johansencff281f2017-01-16 00:42:15 -0800167 */
John Johansen98849df2017-01-16 00:42:16 -0800168struct aa_ns *aa_prepare_ns(const char *name)
John Johansencff281f2017-01-16 00:42:15 -0800169{
John Johansen98849df2017-01-16 00:42:16 -0800170 struct aa_ns *ns, *root;
John Johansencff281f2017-01-16 00:42:15 -0800171
172 root = aa_current_profile()->ns;
173
174 mutex_lock(&root->lock);
175
176 /* if name isn't specified the profile is loaded to the current ns */
177 if (!name) {
178 /* released by caller */
John Johansen98849df2017-01-16 00:42:16 -0800179 ns = aa_get_ns(root);
John Johansencff281f2017-01-16 00:42:15 -0800180 goto out;
181 }
182
183 /* try and find the specified ns and if it doesn't exist create it */
184 /* released by caller */
John Johansen98849df2017-01-16 00:42:16 -0800185 ns = aa_get_ns(__aa_find_ns(&root->sub_ns, name));
John Johansencff281f2017-01-16 00:42:15 -0800186 if (!ns) {
John Johansen98849df2017-01-16 00:42:16 -0800187 ns = alloc_ns(root->base.hname, name);
John Johansencff281f2017-01-16 00:42:15 -0800188 if (!ns)
189 goto out;
John Johansen98849df2017-01-16 00:42:16 -0800190 if (__aa_fs_ns_mkdir(ns, ns_subns_dir(root), name)) {
John Johansencff281f2017-01-16 00:42:15 -0800191 AA_ERROR("Failed to create interface for ns %s\n",
192 ns->base.name);
John Johansen98849df2017-01-16 00:42:16 -0800193 aa_free_ns(ns);
John Johansencff281f2017-01-16 00:42:15 -0800194 ns = NULL;
195 goto out;
196 }
John Johansen98849df2017-01-16 00:42:16 -0800197 ns->parent = aa_get_ns(root);
John Johansencff281f2017-01-16 00:42:15 -0800198 list_add_rcu(&ns->base.list, &root->sub_ns);
199 /* add list ref */
John Johansen98849df2017-01-16 00:42:16 -0800200 aa_get_ns(ns);
John Johansencff281f2017-01-16 00:42:15 -0800201 }
202out:
203 mutex_unlock(&root->lock);
204
205 /* return ref */
206 return ns;
207}
208
209static void __ns_list_release(struct list_head *head);
210
211/**
John Johansen98849df2017-01-16 00:42:16 -0800212 * destroy_ns - remove everything contained by @ns
213 * @ns: ns to have it contents removed (NOT NULL)
John Johansencff281f2017-01-16 00:42:15 -0800214 */
John Johansen98849df2017-01-16 00:42:16 -0800215static void destroy_ns(struct aa_ns *ns)
John Johansencff281f2017-01-16 00:42:15 -0800216{
217 if (!ns)
218 return;
219
220 mutex_lock(&ns->lock);
221 /* release all profiles in this namespace */
222 __aa_profile_list_release(&ns->base.profiles);
223
224 /* release all sub namespaces */
225 __ns_list_release(&ns->sub_ns);
226
227 if (ns->parent)
228 __aa_update_replacedby(ns->unconfined, ns->parent->unconfined);
John Johansen98849df2017-01-16 00:42:16 -0800229 __aa_fs_ns_rmdir(ns);
John Johansencff281f2017-01-16 00:42:15 -0800230 mutex_unlock(&ns->lock);
231}
232
233/**
John Johansen98849df2017-01-16 00:42:16 -0800234 * __aa_remove_ns - remove a namespace and all its children
John Johansencff281f2017-01-16 00:42:15 -0800235 * @ns: namespace to be removed (NOT NULL)
236 *
237 * Requires: ns->parent->lock be held and ns removed from parent.
238 */
John Johansen98849df2017-01-16 00:42:16 -0800239void __aa_remove_ns(struct aa_ns *ns)
John Johansencff281f2017-01-16 00:42:15 -0800240{
241 /* remove ns from namespace list */
242 list_del_rcu(&ns->base.list);
John Johansen98849df2017-01-16 00:42:16 -0800243 destroy_ns(ns);
244 aa_put_ns(ns);
John Johansencff281f2017-01-16 00:42:15 -0800245}
246
247/**
248 * __ns_list_release - remove all profile namespaces on the list put refs
249 * @head: list of profile namespaces (NOT NULL)
250 *
251 * Requires: namespace lock be held
252 */
253static void __ns_list_release(struct list_head *head)
254{
John Johansen98849df2017-01-16 00:42:16 -0800255 struct aa_ns *ns, *tmp;
John Johansencff281f2017-01-16 00:42:15 -0800256
257 list_for_each_entry_safe(ns, tmp, head, base.list)
John Johansen98849df2017-01-16 00:42:16 -0800258 __aa_remove_ns(ns);
John Johansencff281f2017-01-16 00:42:15 -0800259
260}
261
262/**
John Johansen98849df2017-01-16 00:42:16 -0800263 * aa_alloc_root_ns - allocate the root profile namespcae
John Johansencff281f2017-01-16 00:42:15 -0800264 *
265 * Returns: %0 on success else error
266 *
267 */
268int __init aa_alloc_root_ns(void)
269{
270 /* released by aa_free_root_ns - used as list ref*/
John Johansen98849df2017-01-16 00:42:16 -0800271 root_ns = alloc_ns(NULL, "root");
John Johansencff281f2017-01-16 00:42:15 -0800272 if (!root_ns)
273 return -ENOMEM;
274
275 return 0;
276}
277
278 /**
279 * aa_free_root_ns - free the root profile namespace
280 */
281void __init aa_free_root_ns(void)
282{
John Johansen98849df2017-01-16 00:42:16 -0800283 struct aa_ns *ns = root_ns;
John Johansencff281f2017-01-16 00:42:15 -0800284
285 root_ns = NULL;
286
John Johansen98849df2017-01-16 00:42:16 -0800287 destroy_ns(ns);
288 aa_put_ns(ns);
John Johansencff281f2017-01-16 00:42:15 -0800289}