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