]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lsm/apparmor.c
tree-wide: s/getpid()/lxc_raw_getpid()/g
[mirror_lxc.git] / src / lxc / lsm / apparmor.c
1 /* apparmor
2 *
3 * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
4 * Copyright © 2012 Canonical Ltd.
5 *
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.
10
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.
15
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
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mount.h>
28 #include <sys/apparmor.h>
29 #include <sys/vfs.h>
30
31 #include "log.h"
32 #include "lsm/lsm.h"
33 #include "conf.h"
34 #include "utils.h"
35
36 lxc_log_define(lxc_apparmor, lxc);
37
38 /* set by lsm_apparmor_drv_init if true */
39 static int aa_enabled = 0;
40
41 static int mount_features_enabled = 0;
42
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"
48
49 static bool check_mount_feature_enabled(void)
50 {
51 return mount_features_enabled == 1;
52 }
53
54 static void load_mount_features_enabled(void)
55 {
56 struct stat statbuf;
57 int ret;
58
59 ret = stat(AA_MOUNT_RESTR, &statbuf);
60 if (ret == 0)
61 mount_features_enabled = 1;
62 }
63
64 /* aa_getcon is not working right now. Use our hand-rolled version below */
65 static int apparmor_enabled(void)
66 {
67 FILE *fin;
68 char e;
69 int ret;
70
71 fin = fopen(AA_ENABLED_FILE, "r");
72 if (!fin)
73 return 0;
74 ret = fscanf(fin, "%c", &e);
75 fclose(fin);
76 if (ret == 1 && e == 'Y') {
77 load_mount_features_enabled();
78 return 1;
79 }
80
81 return 0;
82 }
83
84 static char *apparmor_process_label_get(pid_t pid)
85 {
86 char path[100], *space;
87 int ret;
88 char *buf = NULL, *newbuf;
89 int sz = 0;
90 FILE *f;
91
92 ret = snprintf(path, 100, "/proc/%d/attr/current", pid);
93 if (ret < 0 || ret >= 100) {
94 ERROR("path name too long");
95 return NULL;
96 }
97 again:
98 f = fopen(path, "r");
99 if (!f) {
100 SYSERROR("opening %s", path);
101 free(buf);
102 return NULL;
103 }
104 sz += 1024;
105 newbuf = realloc(buf, sz);
106 if (!newbuf) {
107 free(buf);
108 ERROR("out of memory");
109 fclose(f);
110 return NULL;
111 }
112 buf = newbuf;
113 memset(buf, 0, sz);
114 ret = fread(buf, 1, sz - 1, f);
115 fclose(f);
116 if (ret < 0) {
117 ERROR("reading %s", path);
118 free(buf);
119 return NULL;
120 }
121 if (ret >= sz)
122 goto again;
123 space = strchr(buf, '\n');
124 if (space)
125 *space = '\0';
126 space = strchr(buf, ' ');
127 if (space)
128 *space = '\0';
129 return buf;
130 }
131
132 /*
133 * Probably makes sense to reorganize these to only read
134 * the label once
135 */
136 static bool apparmor_am_unconfined(void)
137 {
138 char *p = apparmor_process_label_get(lxc_raw_getpid());
139 bool ret = false;
140 if (!p || strcmp(p, "unconfined") == 0)
141 ret = true;
142 free(p);
143 return ret;
144 }
145
146 /* aa stacking is not yet supported */
147 static bool aa_stacking_supported(void) {
148 return false;
149 }
150
151 static bool aa_needs_transition(char *curlabel)
152 {
153 if (!curlabel)
154 return false;
155 if (strcmp(curlabel, "unconfined") == 0)
156 return false;
157 if (strcmp(curlabel, "/usr/bin/lxc-start") == 0)
158 return false;
159 return true;
160 }
161
162 /*
163 * apparmor_process_label_set: Set AppArmor process profile
164 *
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
169 *
170 * Returns 0 on success, < 0 on failure
171 *
172 * Notes: This relies on /proc being available.
173 */
174 static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
175 int use_default, int on_exec)
176 {
177 const char *label = inlabel ? inlabel : conf->lsm_aa_profile;
178 char *curlabel;
179
180 if (!aa_enabled)
181 return 0;
182
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");
186 return 0;
187 }
188
189 curlabel = apparmor_process_label_get(lxc_raw_getpid());
190
191 if (!aa_stacking_supported() && aa_needs_transition(curlabel)) {
192 /* we're already confined, and stacking isn't supported */
193
194 if (!label || strcmp(curlabel, label) == 0) {
195 /* no change requested */
196 free(curlabel);
197 return 0;
198 }
199
200 ERROR("already apparmor confined, but new label requested.");
201 free(curlabel);
202 return -1;
203 }
204 free(curlabel);
205
206 if (!label) {
207 if (use_default) {
208 if (cgns_supported())
209 label = AA_DEF_PROFILE_CGNS;
210 else
211 label = AA_DEF_PROFILE;
212 }
213 else
214 label = "unconfined";
215 }
216
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");
223 return -1;
224 }
225 }
226
227
228 if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
229 INFO("apparmor profile unchanged");
230 return 0;
231 }
232
233 if (aa_change_profile(label) < 0) {
234 SYSERROR("failed to change apparmor profile to %s", label);
235 return -1;
236 }
237
238 INFO("changed apparmor profile to %s", label);
239 return 0;
240 }
241
242 static struct lsm_drv apparmor_drv = {
243 .name = "AppArmor",
244 .enabled = apparmor_enabled,
245 .process_label_get = apparmor_process_label_get,
246 .process_label_set = apparmor_process_label_set,
247 };
248
249 struct lsm_drv *lsm_apparmor_drv_init(void)
250 {
251 if (!apparmor_enabled())
252 return NULL;
253 aa_enabled = 1;
254 return &apparmor_drv;
255 }