]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lsm/apparmor.c
tree-wide: remove unneeded log prefixes
[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 #define _GNU_SOURCE
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/mount.h>
29 #include <sys/vfs.h>
30
31 #include "log.h"
32 #include "lsm.h"
33 #include "conf.h"
34 #include "utils.h"
35
36 lxc_log_define(apparmor, lsm);
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 if @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 bool use_default, bool on_exec)
176 {
177 int label_fd, ret;
178 pid_t tid;
179 const char *label = inlabel ? inlabel : conf->lsm_aa_profile;
180 char *curlabel;
181
182 if (!aa_enabled)
183 return 0;
184
185 /* user may request that we just ignore apparmor */
186 if (label && strcmp(label, AA_UNCHANGED) == 0) {
187 INFO("apparmor profile unchanged per user request");
188 return 0;
189 }
190
191 curlabel = apparmor_process_label_get(lxc_raw_getpid());
192
193 if (!aa_stacking_supported() && aa_needs_transition(curlabel)) {
194 /* we're already confined, and stacking isn't supported */
195
196 if (!label || strcmp(curlabel, label) == 0) {
197 /* no change requested */
198 free(curlabel);
199 return 0;
200 }
201
202 ERROR("already apparmor confined, but new label requested.");
203 free(curlabel);
204 return -1;
205 }
206 free(curlabel);
207
208 if (!label) {
209 if (use_default) {
210 if (cgns_supported())
211 label = AA_DEF_PROFILE_CGNS;
212 else
213 label = AA_DEF_PROFILE;
214 }
215 else
216 label = "unconfined";
217 }
218
219 if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) {
220 WARN("Incomplete AppArmor support in your kernel");
221 if (!conf->lsm_aa_allow_incomplete) {
222 ERROR("If you really want to start this container, set");
223 ERROR("lxc.apparmor.allow_incomplete = 1");
224 ERROR("in your container configuration file");
225 return -1;
226 }
227 }
228
229
230 if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
231 INFO("apparmor profile unchanged");
232 return 0;
233 }
234 tid = lxc_raw_gettid();
235 label_fd = lsm_process_label_fd_get(tid, on_exec);
236 if (label_fd < 0) {
237 SYSERROR("Failed to change apparmor profile to %s", label);
238 return -1;
239 }
240
241 ret = lsm_process_label_set_at(label_fd, label, on_exec);
242 close(label_fd);
243 if (ret < 0) {
244 SYSERROR("Failed to change apparmor profile to %s", label);
245 return -1;
246 }
247
248 INFO("Changed apparmor profile to %s", label);
249 return 0;
250 }
251
252 static struct lsm_drv apparmor_drv = {
253 .name = "AppArmor",
254 .enabled = apparmor_enabled,
255 .process_label_get = apparmor_process_label_get,
256 .process_label_set = apparmor_process_label_set,
257 };
258
259 struct lsm_drv *lsm_apparmor_drv_init(void)
260 {
261 if (!apparmor_enabled())
262 return NULL;
263 aa_enabled = 1;
264 return &apparmor_drv;
265 }