chiark / gitweb /
Add support for ConditionSecurity=ima
[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         if (streq(parameter, "ima"))
168                 return access("/sys/kernel/security/ima/", F_OK) == 0;
169         if (streq(parameter, "smack"))
170                 return access("/sys/fs/smackfs", F_OK) == 0;
171         return false;
172 }
173
174 static bool test_capability(const char *parameter) {
175         cap_value_t value;
176         FILE *f;
177         char line[LINE_MAX];
178         unsigned long long capabilities = (unsigned long long) -1;
179
180         /* If it's an invalid capability, we don't have it */
181
182         if (cap_from_name(parameter, &value) < 0)
183                 return false;
184
185         /* If it's a valid capability we default to assume
186          * that we have it */
187
188         f = fopen("/proc/self/status", "re");
189         if (!f)
190                 return true;
191
192         while (fgets(line, sizeof(line), f)) {
193                 truncate_nl(line);
194
195                 if (startswith(line, "CapBnd:")) {
196                         (void) sscanf(line+7, "%llx", &capabilities);
197                         break;
198                 }
199         }
200
201         fclose(f);
202
203         return !!(capabilities & (1ULL << value));
204 }
205
206 static bool test_host(const char *parameter) {
207         sd_id128_t x, y;
208         char *h;
209         int r;
210         bool b;
211
212         if (sd_id128_from_string(parameter, &x) >= 0) {
213
214                 r = sd_id128_get_machine(&y);
215                 if (r < 0)
216                         return false;
217
218                 return sd_id128_equal(x, y);
219         }
220
221         h = gethostname_malloc();
222         if (!h)
223                 return false;
224
225         b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
226         free(h);
227
228         return b;
229 }
230
231 static bool test_ac_power(const char *parameter) {
232         int r;
233
234         r = parse_boolean(parameter);
235         if (r < 0)
236                 return true;
237
238         return (on_ac_power() != 0) == !!r;
239 }
240
241 bool condition_test(Condition *c) {
242         assert(c);
243
244         switch(c->type) {
245
246         case CONDITION_PATH_EXISTS:
247                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
248
249         case CONDITION_PATH_EXISTS_GLOB:
250                 return (glob_exists(c->parameter) > 0) == !c->negate;
251
252         case CONDITION_PATH_IS_DIRECTORY: {
253                 struct stat st;
254
255                 if (stat(c->parameter, &st) < 0)
256                         return c->negate;
257                 return S_ISDIR(st.st_mode) == !c->negate;
258         }
259
260         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
261                 struct stat st;
262
263                 if (lstat(c->parameter, &st) < 0)
264                         return c->negate;
265                 return S_ISLNK(st.st_mode) == !c->negate;
266         }
267
268         case CONDITION_PATH_IS_MOUNT_POINT:
269                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
270
271         case CONDITION_PATH_IS_READ_WRITE:
272                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
273
274         case CONDITION_DIRECTORY_NOT_EMPTY: {
275                 int k;
276
277                 k = dir_is_empty(c->parameter);
278                 return !(k == -ENOENT || k > 0) == !c->negate;
279         }
280
281         case CONDITION_FILE_NOT_EMPTY: {
282                 struct stat st;
283
284                 if (stat(c->parameter, &st) < 0)
285                         return c->negate;
286
287                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
288         }
289
290         case CONDITION_FILE_IS_EXECUTABLE: {
291                 struct stat st;
292
293                 if (stat(c->parameter, &st) < 0)
294                         return c->negate;
295
296                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
297         }
298
299         case CONDITION_KERNEL_COMMAND_LINE:
300                 return test_kernel_command_line(c->parameter) == !c->negate;
301
302         case CONDITION_VIRTUALIZATION:
303                 return test_virtualization(c->parameter) == !c->negate;
304
305         case CONDITION_SECURITY:
306                 return test_security(c->parameter) == !c->negate;
307
308         case CONDITION_CAPABILITY:
309                 return test_capability(c->parameter) == !c->negate;
310
311         case CONDITION_HOST:
312                 return test_host(c->parameter) == !c->negate;
313
314         case CONDITION_AC_POWER:
315                 return test_ac_power(c->parameter) == !c->negate;
316
317         case CONDITION_NULL:
318                 return !c->negate;
319
320         default:
321                 assert_not_reached("Invalid condition type.");
322         }
323 }
324
325 bool condition_test_list(Condition *first) {
326         Condition *c;
327         int triggered = -1;
328
329         /* If the condition list is empty, then it is true */
330         if (!first)
331                 return true;
332
333         /* Otherwise, if all of the non-trigger conditions apply and
334          * if any of the trigger conditions apply (unless there are
335          * none) we return true */
336         LIST_FOREACH(conditions, c, first) {
337                 bool b;
338
339                 b = condition_test(c);
340
341                 if (!c->trigger && !b)
342                         return false;
343
344                 if (c->trigger && triggered <= 0)
345                         triggered = b;
346         }
347
348         return triggered != 0;
349 }
350
351 void condition_dump(Condition *c, FILE *f, const char *prefix) {
352         assert(c);
353         assert(f);
354
355         if (!prefix)
356                 prefix = "";
357
358         fprintf(f,
359                 "%s\t%s: %s%s%s\n",
360                 prefix,
361                 condition_type_to_string(c->type),
362                 c->trigger ? "|" : "",
363                 c->negate ? "!" : "",
364                 c->parameter);
365 }
366
367 void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
368         Condition *c;
369
370         LIST_FOREACH(conditions, c, first)
371                 condition_dump(c, f, prefix);
372 }
373
374 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
375         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
376         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
377         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
378         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
379         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
380         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
381         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
382         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
383         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
384         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
385         [CONDITION_SECURITY] = "ConditionSecurity",
386         [CONDITION_HOST] = "ConditionHost",
387         [CONDITION_AC_POWER] = "ConditionACPower",
388         [CONDITION_NULL] = "ConditionNull"
389 };
390
391 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);