3 * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
4 * Copyright © 2012 Canonical Ltd.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <sys/types.h>
27 #include <sys/mount.h>
28 #include <sys/apparmor.h>
36 lxc_log_define(lxc_apparmor
, lxc
);
38 /* set by lsm_apparmor_drv_init if true */
39 static int aa_enabled
= 0;
41 static int mount_features_enabled
= 0;
43 #define AA_DEF_PROFILE "lxc-container-default"
44 #define AA_DEF_PROFILE_CGNS "lxc-container-default-cgns"
45 #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
46 #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
47 #define AA_UNCHANGED "unchanged"
49 static bool check_mount_feature_enabled(void)
51 return mount_features_enabled
== 1;
54 static void load_mount_features_enabled(void)
59 ret
= stat(AA_MOUNT_RESTR
, &statbuf
);
61 mount_features_enabled
= 1;
64 /* aa_getcon is not working right now. Use our hand-rolled version below */
65 static int apparmor_enabled(void)
71 fin
= fopen(AA_ENABLED_FILE
, "r");
74 ret
= fscanf(fin
, "%c", &e
);
76 if (ret
== 1 && e
== 'Y') {
77 load_mount_features_enabled();
84 static char *apparmor_process_label_get(pid_t pid
)
86 char path
[100], *space
;
88 char *buf
= NULL
, *newbuf
;
92 ret
= snprintf(path
, 100, "/proc/%d/attr/current", pid
);
93 if (ret
< 0 || ret
>= 100) {
94 ERROR("path name too long");
100 SYSERROR("opening %s", path
);
105 newbuf
= realloc(buf
, sz
);
108 ERROR("out of memory");
114 ret
= fread(buf
, 1, sz
- 1, f
);
117 ERROR("reading %s", path
);
123 space
= strchr(buf
, '\n');
126 space
= strchr(buf
, ' ');
133 * Probably makes sense to reorganize these to only read
136 static bool apparmor_am_unconfined(void)
138 char *p
= apparmor_process_label_get(lxc_raw_getpid());
140 if (!p
|| strcmp(p
, "unconfined") == 0)
146 /* aa stacking is not yet supported */
147 static bool aa_stacking_supported(void) {
151 static bool aa_needs_transition(char *curlabel
)
155 if (strcmp(curlabel
, "unconfined") == 0)
157 if (strcmp(curlabel
, "/usr/bin/lxc-start") == 0)
163 * apparmor_process_label_set: Set AppArmor process profile
165 * @label : the profile to set
166 * @conf : the container configuration to use @label is NULL
167 * @default : use the default profile if label is NULL
168 * @on_exec : this is ignored. Apparmor profile will be changed immediately
170 * Returns 0 on success, < 0 on failure
172 * Notes: This relies on /proc being available.
174 static int apparmor_process_label_set(const char *inlabel
, struct lxc_conf
*conf
,
175 int use_default
, int on_exec
)
177 const char *label
= inlabel
? inlabel
: conf
->lsm_aa_profile
;
183 /* user may request that we just ignore apparmor */
184 if (label
&& strcmp(label
, AA_UNCHANGED
) == 0) {
185 INFO("apparmor profile unchanged per user request");
189 curlabel
= apparmor_process_label_get(lxc_raw_getpid());
191 if (!aa_stacking_supported() && aa_needs_transition(curlabel
)) {
192 /* we're already confined, and stacking isn't supported */
194 if (!label
|| strcmp(curlabel
, label
) == 0) {
195 /* no change requested */
200 ERROR("already apparmor confined, but new label requested.");
208 if (cgns_supported())
209 label
= AA_DEF_PROFILE_CGNS
;
211 label
= AA_DEF_PROFILE
;
214 label
= "unconfined";
217 if (!check_mount_feature_enabled() && strcmp(label
, "unconfined") != 0) {
218 WARN("Incomplete AppArmor support in your kernel");
219 if (!conf
->lsm_aa_allow_incomplete
) {
220 ERROR("If you really want to start this container, set");
221 ERROR("lxc.apparmor.allow_incomplete = 1");
222 ERROR("in your container configuration file");
228 if (strcmp(label
, "unconfined") == 0 && apparmor_am_unconfined()) {
229 INFO("apparmor profile unchanged");
233 if (aa_change_profile(label
) < 0) {
234 SYSERROR("failed to change apparmor profile to %s", label
);
238 INFO("changed apparmor profile to %s", label
);
242 static struct lsm_drv apparmor_drv
= {
244 .enabled
= apparmor_enabled
,
245 .process_label_get
= apparmor_process_label_get
,
246 .process_label_set
= apparmor_process_label_set
,
249 struct lsm_drv
*lsm_apparmor_drv_init(void)
251 if (!apparmor_enabled())
254 return &apparmor_drv
;