chiark / gitweb /
condition, man: Add support for ConditionSecurity=apparmor
[elogind.git] / src / core / condition.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/capability.h>
27 #include <sys/statvfs.h>
28 #include <fnmatch.h>
29
30 #ifdef HAVE_SELINUX
31 #include <selinux/selinux.h>
32 #endif
33
34 #include <systemd/sd-id128.h>
35 #include "util.h"
36 #include "condition.h"
37 #include "virt.h"
38 #include "path-util.h"
39 #include "fileio.h"
40
41 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
42         Condition *c;
43
44         assert(type < _CONDITION_TYPE_MAX);
45
46         c = new0(Condition, 1);
47         if (!c)
48                 return NULL;
49
50         c->type = type;
51         c->trigger = trigger;
52         c->negate = negate;
53
54         if (parameter) {
55                 c->parameter = strdup(parameter);
56                 if (!c->parameter) {
57                         free(c);
58                         return NULL;
59                 }
60         }
61
62         return c;
63 }
64
65 void condition_free(Condition *c) {
66         assert(c);
67
68         free(c->parameter);
69         free(c);
70 }
71
72 void condition_free_list(Condition *first) {
73         Condition *c, *n;
74
75         LIST_FOREACH_SAFE(conditions, c, n, first)
76                 condition_free(c);
77 }
78
79 static bool test_kernel_command_line(const char *parameter) {
80         char *line, *w, *state, *word = NULL;
81         bool equal;
82         int r;
83         size_t l, pl;
84         bool found = false;
85
86         assert(parameter);
87
88         if (detect_container(NULL) > 0)
89                 return false;
90
91         r = read_one_line_file("/proc/cmdline", &line);
92         if (r < 0) {
93                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
94                 return false;
95         }
96
97         equal = !!strchr(parameter, '=');
98         pl = strlen(parameter);
99
100         FOREACH_WORD_QUOTED(w, l, line, state) {
101
102                 free(word);
103                 word = strndup(w, l);
104                 if (!word)
105                         break;
106
107                 if (equal) {
108                         if (streq(word, parameter)) {
109                                 found = true;
110                                 break;
111                         }
112                 } else {
113                         if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
114                                 found = true;
115                                 break;
116                         }
117                 }
118
119         }
120
121         free(word);
122         free(line);
123
124         return found;
125 }
126
127 static bool test_virtualization(const char *parameter) {
128         int b;
129         Virtualization v;
130         const char *id;
131
132         assert(parameter);
133
134         v = detect_virtualization(&id);
135         if (v < 0) {
136                 log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
137                 return false;
138         }
139
140         /* First, compare with yes/no */
141         b = parse_boolean(parameter);
142
143         if (v > 0 && b > 0)
144                 return true;
145
146         if (v == 0 && b == 0)
147                 return true;
148
149         /* Then, compare categorization */
150         if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
151                 return true;
152
153         if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
154                 return true;
155
156         /* Finally compare id */
157         return v > 0 && streq(parameter, id);
158 }
159
160 static bool test_security(const char *parameter) {
161 #ifdef HAVE_SELINUX
162         if (streq(parameter, "selinux"))
163                 return is_selinux_enabled() > 0;
164 #endif
165         if (streq(parameter, "apparmor"))
166                 return access("/sys/kernel/security/apparmor/", F_OK) == 0;
167         return false;
168 }
169
170 static bool test_capability(const char *parameter) {
171         cap_value_t value;
172         FILE *f;
173         char line[LINE_MAX];
174         unsigned long long capabilities = (unsigned long long) -1;
175
176         /* If it's an invalid capability, we don't have it */
177
178         if (cap_from_name(parameter, &value) < 0)
179                 return false;
180
181         /* If it's a valid capability we default to assume
182          * that we have it */
183
184         f = fopen("/proc/self/status", "re");
185         if (!f)
186                 return true;
187
188         while (fgets(line, sizeof(line), f)) {
189                 truncate_nl(line);
190
191                 if (startswith(line, "CapBnd:")) {
192                         (void) sscanf(line+7, "%llx", &capabilities);
193                         break;
194                 }
195         }
196
197         fclose(f);
198
199         return !!(capabilities & (1ULL << value));
200 }
201
202 static bool test_host(const char *parameter) {
203         sd_id128_t x, y;
204         char *h;
205         int r;
206         bool b;
207
208         if (sd_id128_from_string(parameter, &x) >= 0) {
209
210                 r = sd_id128_get_machine(&y);
211                 if (r < 0)
212                         return false;
213
214                 return sd_id128_equal(x, y);
215         }
216
217         h = gethostname_malloc();
218         if (!h)
219                 return false;
220
221         b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
222         free(h);
223
224         return b;
225 }
226
227 static bool test_ac_power(const char *parameter) {
228         int r;
229
230         r = parse_boolean(parameter);
231         if (r < 0)
232                 return true;
233
234         return (on_ac_power() != 0) == !!r;
235 }
236
237 bool condition_test(Condition *c) {
238         assert(c);
239
240         switch(c->type) {
241
242         case CONDITION_PATH_EXISTS:
243                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
244
245         case CONDITION_PATH_EXISTS_GLOB:
246                 return (glob_exists(c->parameter) > 0) == !c->negate;
247
248         case CONDITION_PATH_IS_DIRECTORY: {
249                 struct stat st;
250
251                 if (stat(c->parameter, &st) < 0)
252                         return c->negate;
253                 return S_ISDIR(st.st_mode) == !c->negate;
254         }
255
256         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
257                 struct stat st;
258
259                 if (lstat(c->parameter, &st) < 0)
260                         return c->negate;
261                 return S_ISLNK(st.st_mode) == !c->negate;
262         }
263
264         case CONDITION_PATH_IS_MOUNT_POINT:
265                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
266
267         case CONDITION_PATH_IS_READ_WRITE:
268                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
269
270         case CONDITION_DIRECTORY_NOT_EMPTY: {
271                 int k;
272
273                 k = dir_is_empty(c->parameter);
274                 return !(k == -ENOENT || k > 0) == !c->negate;
275         }
276
277         case CONDITION_FILE_NOT_EMPTY: {
278                 struct stat st;
279
280                 if (stat(c->parameter, &st) < 0)
281                         return c->negate;
282
283                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
284         }
285
286         case CONDITION_FILE_IS_EXECUTABLE: {
287                 struct stat st;
288
289                 if (stat(c->parameter, &st) < 0)
290                         return c->negate;
291
292                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
293         }
294
295         case CONDITION_KERNEL_COMMAND_LINE:
296                 return test_kernel_command_line(c->parameter) == !c->negate;
297
298         case CONDITION_VIRTUALIZATION:
299                 return test_virtualization(c->parameter) == !c->negate;
300
301         case CONDITION_SECURITY:
302                 return test_security(c->parameter) == !c->negate;
303
304         case CONDITION_CAPABILITY:
305                 return test_capability(c->parameter) == !c->negate;
306
307         case CONDITION_HOST:
308                 return test_host(c->parameter) == !c->negate;
309
310         case CONDITION_AC_POWER:
311                 return test_ac_power(c->parameter) == !c->negate;
312
313         case CONDITION_NULL:
314                 return !c->negate;
315
316         default:
317                 assert_not_reached("Invalid condition type.");
318         }
319 }
320
321 bool condition_test_list(Condition *first) {
322         Condition *c;
323         int triggered = -1;
324
325         /* If the condition list is empty, then it is true */
326         if (!first)
327                 return true;
328
329         /* Otherwise, if all of the non-trigger conditions apply and
330          * if any of the trigger conditions apply (unless there are
331          * none) we return true */
332         LIST_FOREACH(conditions, c, first) {
333                 bool b;
334
335                 b = condition_test(c);
336
337                 if (!c->trigger && !b)
338                         return false;
339
340                 if (c->trigger && triggered <= 0)
341                         triggered = b;
342         }
343
344         return triggered != 0;
345 }
346
347 void condition_dump(Condition *c, FILE *f, const char *prefix) {
348         assert(c);
349         assert(f);
350
351         if (!prefix)
352                 prefix = "";
353
354         fprintf(f,
355                 "%s\t%s: %s%s%s\n",
356                 prefix,
357                 condition_type_to_string(c->type),
358                 c->trigger ? "|" : "",
359                 c->negate ? "!" : "",
360                 c->parameter);
361 }
362
363 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
364         Condition *c;
365
366         LIST_FOREACH(conditions, c, first)
367                 condition_dump(c, f, prefix);
368 }
369
370 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
371         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
372         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
373         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
374         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
375         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
376         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
377         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
378         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
379         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
380         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
381         [CONDITION_SECURITY] = "ConditionSecurity",
382         [CONDITION_HOST] = "ConditionHost",
383         [CONDITION_AC_POWER] = "ConditionACPower",
384         [CONDITION_NULL] = "ConditionNull"
385 };
386
387 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);