chiark / gitweb /
5db20a7085d393c3b4cad65dfd15f6a7729cce7c
[elogind.git] / src / shared / condition-util.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/statvfs.h>
27 #include <fnmatch.h>
28
29 #include "sd-id128.h"
30 #include "util.h"
31 #include "condition-util.h"
32 #include "virt.h"
33 #include "path-util.h"
34 #include "fileio.h"
35 #include "unit.h"
36 #include "architecture.h"
37 #include "virt.h"
38 #include "smack-util.h"
39 #include "apparmor-util.h"
40 #include "ima-util.h"
41 #include "selinux-util.h"
42 #include "audit.h"
43
44 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
45         Condition *c;
46         int r;
47
48         assert(type >= 0);
49         assert(type < _CONDITION_TYPE_MAX);
50         assert(!parameter == (type == CONDITION_NULL));
51
52         c = new0(Condition, 1);
53         if (!c)
54                 return NULL;
55
56         c->type = type;
57         c->trigger = trigger;
58         c->negate = negate;
59
60         r = free_and_strdup(&c->parameter, parameter);
61         if (r < 0) {
62                 free(c);
63                 return NULL;
64         }
65
66         return c;
67 }
68
69 void condition_free(Condition *c) {
70         assert(c);
71
72         free(c->parameter);
73         free(c);
74 }
75
76 void condition_free_list(Condition *first) {
77         Condition *c, *n;
78
79         LIST_FOREACH_SAFE(conditions, c, n, first)
80                 condition_free(c);
81 }
82
83 static int condition_test_kernel_command_line(Condition *c) {
84         _cleanup_free_ char *line = NULL;
85         const char *p;
86         bool equal;
87         int r;
88
89         assert(c);
90         assert(c->parameter);
91         assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
92
93         r = proc_cmdline(&line);
94         if (r < 0)
95                 return r;
96         if (r == 0)
97                 return false;
98
99         equal = !!strchr(c->parameter, '=');
100         p = line;
101
102         for (;;) {
103                 _cleanup_free_ char *word = NULL;
104                 bool found;
105
106                 r = unquote_first_word(&p, &word);
107                 if (r < 0)
108                         return r;
109                 if (r == 0)
110                         break;
111
112                 if (equal)
113                         found = streq(word, c->parameter);
114                 else {
115                         const char *f;
116
117                         f = startswith(word, c->parameter);
118                         found = f && (*f == '=' || *f == 0);
119                 }
120
121                 if (found)
122                         return true;
123         }
124
125         return false;
126 }
127
128 static int condition_test_virtualization(Condition *c) {
129         int b, v;
130         const char *id;
131
132         assert(c);
133         assert(c->parameter);
134         assert(c->type == CONDITION_VIRTUALIZATION);
135
136         v = detect_virtualization(&id);
137         if (v < 0)
138                 return v;
139
140         /* First, compare with yes/no */
141         b = parse_boolean(c->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(c->parameter, "vm"))
151                 return true;
152
153         if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container"))
154                 return true;
155
156         /* Finally compare id */
157         return v > 0 && streq(c->parameter, id);
158 }
159
160 static int condition_test_architecture(Condition *c) {
161         int a, b;
162
163         assert(c);
164         assert(c->parameter);
165         assert(c->type == CONDITION_ARCHITECTURE);
166
167         a = uname_architecture();
168         if (a < 0)
169                 return a;
170
171         if (streq(c->parameter, "native"))
172                 b = native_architecture();
173         else
174                 b = architecture_from_string(c->parameter);
175         if (b < 0)
176                 return b;
177
178         return a == b;
179 }
180
181 static int condition_test_host(Condition *c) {
182         _cleanup_free_ char *h = NULL;
183         sd_id128_t x, y;
184         int r;
185
186         assert(c);
187         assert(c->parameter);
188         assert(c->type == CONDITION_HOST);
189
190         if (sd_id128_from_string(c->parameter, &x) >= 0) {
191
192                 r = sd_id128_get_machine(&y);
193                 if (r < 0)
194                         return r;
195
196                 return sd_id128_equal(x, y);
197         }
198
199         h = gethostname_malloc();
200         if (!h)
201                 return -ENOMEM;
202
203         return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
204 }
205
206 static int condition_test_ac_power(Condition *c) {
207         int r;
208
209         assert(c);
210         assert(c->parameter);
211         assert(c->type == CONDITION_AC_POWER);
212
213         r = parse_boolean(c->parameter);
214         if (r < 0)
215                 return r;
216
217         return (on_ac_power() != 0) == !!r;
218 }
219
220 static int condition_test_security(Condition *c) {
221         assert(c);
222         assert(c->parameter);
223         assert(c->type == CONDITION_SECURITY);
224
225         if (streq(c->parameter, "selinux"))
226                 return mac_selinux_use();
227         if (streq(c->parameter, "smack"))
228                 return mac_smack_use();
229         if (streq(c->parameter, "apparmor"))
230                 return mac_apparmor_use();
231         if (streq(c->parameter, "audit"))
232                 return use_audit();
233         if (streq(c->parameter, "ima"))
234                 return use_ima();
235
236         return false;
237 }
238
239 static int condition_test_capability(Condition *c) {
240         _cleanup_fclose_ FILE *f = NULL;
241         cap_value_t value;
242         char line[LINE_MAX];
243         unsigned long long capabilities = -1;
244
245         assert(c);
246         assert(c->parameter);
247         assert(c->type == CONDITION_CAPABILITY);
248
249         /* If it's an invalid capability, we don't have it */
250
251         if (cap_from_name(c->parameter, &value) < 0)
252                 return -EINVAL;
253
254         /* If it's a valid capability we default to assume
255          * that we have it */
256
257         f = fopen("/proc/self/status", "re");
258         if (!f)
259                 return -errno;
260
261         while (fgets(line, sizeof(line), f)) {
262                 truncate_nl(line);
263
264                 if (startswith(line, "CapBnd:")) {
265                         (void) sscanf(line+7, "%llx", &capabilities);
266                         break;
267                 }
268         }
269
270         return !!(capabilities & (1ULL << value));
271 }
272
273 static int condition_test_needs_update(Condition *c) {
274         const char *p;
275         struct stat usr, other;
276
277         assert(c);
278         assert(c->parameter);
279         assert(c->type == CONDITION_NEEDS_UPDATE);
280
281         /* If the file system is read-only we shouldn't suggest an update */
282         if (path_is_read_only_fs(c->parameter) > 0)
283                 return false;
284
285         /* Any other failure means we should allow the condition to be true,
286          * so that we rather invoke too many update tools then too
287          * few. */
288
289         if (!path_is_absolute(c->parameter))
290                 return true;
291
292         p = strappenda(c->parameter, "/.updated");
293         if (lstat(p, &other) < 0)
294                 return true;
295
296         if (lstat("/usr/", &usr) < 0)
297                 return true;
298
299         return usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
300                 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec);
301 }
302
303 static int condition_test_first_boot(Condition *c) {
304         int r;
305
306         assert(c);
307         assert(c->parameter);
308         assert(c->type == CONDITION_FIRST_BOOT);
309
310         r = parse_boolean(c->parameter);
311         if (r < 0)
312                 return r;
313
314         return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
315 }
316
317 static int condition_test_path_exists(Condition *c) {
318         assert(c);
319         assert(c->parameter);
320         assert(c->type == CONDITION_PATH_EXISTS);
321
322         return access(c->parameter, F_OK) >= 0;
323 }
324
325 static int condition_test_path_exists_glob(Condition *c) {
326         assert(c);
327         assert(c->parameter);
328         assert(c->type == CONDITION_PATH_EXISTS_GLOB);
329
330         return glob_exists(c->parameter) > 0;
331 }
332
333 static int condition_test_path_is_directory(Condition *c) {
334         assert(c);
335         assert(c->parameter);
336         assert(c->type == CONDITION_PATH_IS_DIRECTORY);
337
338         return is_dir(c->parameter, true) > 0;
339 }
340
341 static int condition_test_path_is_symbolic_link(Condition *c) {
342         assert(c);
343         assert(c->parameter);
344         assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
345
346         return is_symlink(c->parameter) > 0;
347 }
348
349 static int condition_test_path_is_mount_point(Condition *c) {
350         assert(c);
351         assert(c->parameter);
352         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
353
354         return path_is_mount_point(c->parameter, true) > 0;
355 }
356
357 static int condition_test_path_is_read_write(Condition *c) {
358         assert(c);
359         assert(c->parameter);
360         assert(c->type == CONDITION_PATH_IS_READ_WRITE);
361
362         return path_is_read_only_fs(c->parameter) <= 0;
363 }
364
365 static int condition_test_directory_not_empty(Condition *c) {
366         int r;
367
368         assert(c);
369         assert(c->parameter);
370         assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
371
372         r = dir_is_empty(c->parameter);
373         return r <= 0 && r != -ENOENT;
374 }
375
376 static int condition_test_file_not_empty(Condition *c) {
377         struct stat st;
378
379         assert(c);
380         assert(c->parameter);
381         assert(c->type == CONDITION_FILE_NOT_EMPTY);
382
383         return (stat(c->parameter, &st) >= 0 &&
384                 S_ISREG(st.st_mode) &&
385                 st.st_size > 0);
386 }
387
388 static int condition_test_file_is_executable(Condition *c) {
389         struct stat st;
390
391         assert(c);
392         assert(c->parameter);
393         assert(c->type == CONDITION_FILE_IS_EXECUTABLE);
394
395         return (stat(c->parameter, &st) >= 0 &&
396                 S_ISREG(st.st_mode) &&
397                 (st.st_mode & 0111));
398 }
399
400 static int condition_test_null(Condition *c) {
401         assert(c);
402         assert(c->type == CONDITION_NULL);
403
404         /* Note that during parsing we already evaluate the string and
405          * store it in c->negate */
406         return true;
407 }
408
409 int condition_test(Condition *c) {
410
411         static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
412                 [CONDITION_PATH_EXISTS] = condition_test_path_exists,
413                 [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
414                 [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
415                 [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
416                 [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
417                 [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
418                 [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
419                 [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
420                 [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
421                 [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
422                 [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
423                 [CONDITION_SECURITY] = condition_test_security,
424                 [CONDITION_CAPABILITY] = condition_test_capability,
425                 [CONDITION_HOST] = condition_test_host,
426                 [CONDITION_AC_POWER] = condition_test_ac_power,
427                 [CONDITION_ARCHITECTURE] = condition_test_architecture,
428                 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
429                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
430                 [CONDITION_NULL] = condition_test_null,
431         };
432
433         int r, b;
434
435         assert(c);
436         assert(c->type >= 0);
437         assert(c->type < _CONDITION_TYPE_MAX);
438
439         r = condition_tests[c->type](c);
440         if (r < 0) {
441                 c->result = CONDITION_ERROR;
442                 return r;
443         }
444
445         b = (r > 0) == !c->negate;
446         c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED;
447         return b;
448 }
449
450 void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
451         assert(c);
452         assert(f);
453
454         if (!prefix)
455                 prefix = "";
456
457         fprintf(f,
458                 "%s\t%s: %s%s%s %s\n",
459                 prefix,
460                 to_string(c->type),
461                 c->trigger ? "|" : "",
462                 c->negate ? "!" : "",
463                 c->parameter,
464                 condition_result_to_string(c->result));
465 }
466
467 void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
468         Condition *c;
469
470         LIST_FOREACH(conditions, c, first)
471                 condition_dump(c, f, prefix, to_string);
472 }
473
474 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
475         [CONDITION_ARCHITECTURE] = "ConditionArchitecture",
476         [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
477         [CONDITION_HOST] = "ConditionHost",
478         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
479         [CONDITION_SECURITY] = "ConditionSecurity",
480         [CONDITION_CAPABILITY] = "ConditionCapability",
481         [CONDITION_AC_POWER] = "ConditionACPower",
482         [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate",
483         [CONDITION_FIRST_BOOT] = "ConditionFirstBoot",
484         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
485         [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
486         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
487         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
488         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
489         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
490         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
491         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
492         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
493         [CONDITION_NULL] = "ConditionNull"
494 };
495
496 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
497
498 static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
499         [CONDITION_ARCHITECTURE] = "AssertArchitecture",
500         [CONDITION_VIRTUALIZATION] = "AssertVirtualization",
501         [CONDITION_HOST] = "AssertHost",
502         [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
503         [CONDITION_SECURITY] = "AssertSecurity",
504         [CONDITION_CAPABILITY] = "AssertCapability",
505         [CONDITION_AC_POWER] = "AssertACPower",
506         [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate",
507         [CONDITION_FIRST_BOOT] = "AssertFirstBoot",
508         [CONDITION_PATH_EXISTS] = "AssertPathExists",
509         [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob",
510         [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory",
511         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
512         [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
513         [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
514         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
515         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
516         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
517         [CONDITION_NULL] = "AssertNull"
518 };
519
520 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
521
522 static const char* const condition_result_table[_CONDITION_RESULT_MAX] = {
523         [CONDITION_UNTESTED] = "untested",
524         [CONDITION_SUCCEEDED] = "succeeded",
525         [CONDITION_FAILED] = "failed",
526         [CONDITION_ERROR] = "error",
527 };
528
529 DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult);