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