chiark / gitweb /
core: introduce ConditionSecurity=audit
[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 #include "sd-id128.h"
31 #include "util.h"
32 #include "condition.h"
33 #include "virt.h"
34 #include "path-util.h"
35 #include "fileio.h"
36 #include "unit.h"
37 #include "smack-util.h"
38 #include "apparmor-util.h"
39 #include "ima-util.h"
40 #include "selinux-util.h"
41 #include "audit.h"
42
43 static bool condition_test_security(Condition *c) {
44         assert(c);
45         assert(c->parameter);
46         assert(c->type == CONDITION_SECURITY);
47
48         if (streq(c->parameter, "selinux"))
49                 return mac_selinux_use() == !c->negate;
50         if (streq(c->parameter, "smack"))
51                 return mac_smack_use() == !c->negate;
52         if (streq(c->parameter, "apparmor"))
53                 return mac_apparmor_use() == !c->negate;
54         if (streq(c->parameter, "audit"))
55                 return use_audit() == !c->negate;
56         if (streq(c->parameter, "ima"))
57                 return use_ima() == !c->negate;
58
59         return c->negate;
60 }
61
62 static bool condition_test_capability(Condition *c) {
63         _cleanup_fclose_ FILE *f = NULL;
64         cap_value_t value;
65         char line[LINE_MAX];
66         unsigned long long capabilities = -1;
67
68         assert(c);
69         assert(c->parameter);
70         assert(c->type == CONDITION_CAPABILITY);
71
72         /* If it's an invalid capability, we don't have it */
73
74         if (cap_from_name(c->parameter, &value) < 0)
75                 return c->negate;
76
77         /* If it's a valid capability we default to assume
78          * that we have it */
79
80         f = fopen("/proc/self/status", "re");
81         if (!f)
82                 return !c->negate;
83
84         while (fgets(line, sizeof(line), f)) {
85                 truncate_nl(line);
86
87                 if (startswith(line, "CapBnd:")) {
88                         (void) sscanf(line+7, "%llx", &capabilities);
89                         break;
90                 }
91         }
92
93         return !!(capabilities & (1ULL << value)) == !c->negate;
94 }
95
96 static bool condition_test_needs_update(Condition *c) {
97         const char *p;
98         struct stat usr, other;
99
100         assert(c);
101         assert(c->parameter);
102         assert(c->type == CONDITION_NEEDS_UPDATE);
103
104         /* If the file system is read-only we shouldn't suggest an update */
105         if (path_is_read_only_fs(c->parameter) > 0)
106                 return c->negate;
107
108         /* Any other failure means we should allow the condition to be true,
109          * so that we rather invoke too many update tools then too
110          * few. */
111
112         if (!path_is_absolute(c->parameter))
113                 return !c->negate;
114
115         p = strappenda(c->parameter, "/.updated");
116         if (lstat(p, &other) < 0)
117                 return !c->negate;
118
119         if (lstat("/usr/", &usr) < 0)
120                 return !c->negate;
121
122         return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec ||
123                 (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate;
124 }
125
126 static bool condition_test_first_boot(Condition *c) {
127         int r;
128
129         assert(c);
130         assert(c->parameter);
131         assert(c->type == CONDITION_FIRST_BOOT);
132
133         r = parse_boolean(c->parameter);
134         if (r < 0)
135                 return c->negate;
136
137         return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate;
138 }
139
140 static bool condition_test(Condition *c) {
141         assert(c);
142
143         switch(c->type) {
144
145         case CONDITION_PATH_EXISTS:
146                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
147
148         case CONDITION_PATH_EXISTS_GLOB:
149                 return (glob_exists(c->parameter) > 0) == !c->negate;
150
151         case CONDITION_PATH_IS_DIRECTORY: {
152                 struct stat st;
153
154                 if (stat(c->parameter, &st) < 0)
155                         return c->negate;
156                 return S_ISDIR(st.st_mode) == !c->negate;
157         }
158
159         case CONDITION_PATH_IS_SYMBOLIC_LINK: {
160                 struct stat st;
161
162                 if (lstat(c->parameter, &st) < 0)
163                         return c->negate;
164                 return S_ISLNK(st.st_mode) == !c->negate;
165         }
166
167         case CONDITION_PATH_IS_MOUNT_POINT:
168                 return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
169
170         case CONDITION_PATH_IS_READ_WRITE:
171                 return (path_is_read_only_fs(c->parameter) > 0) == c->negate;
172
173         case CONDITION_DIRECTORY_NOT_EMPTY: {
174                 int k;
175
176                 k = dir_is_empty(c->parameter);
177                 return !(k == -ENOENT || k > 0) == !c->negate;
178         }
179
180         case CONDITION_FILE_NOT_EMPTY: {
181                 struct stat st;
182
183                 if (stat(c->parameter, &st) < 0)
184                         return c->negate;
185
186                 return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
187         }
188
189         case CONDITION_FILE_IS_EXECUTABLE: {
190                 struct stat st;
191
192                 if (stat(c->parameter, &st) < 0)
193                         return c->negate;
194
195                 return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
196         }
197
198         case CONDITION_KERNEL_COMMAND_LINE:
199                 return condition_test_kernel_command_line(c);
200
201         case CONDITION_VIRTUALIZATION:
202                 return condition_test_virtualization(c);
203
204         case CONDITION_SECURITY:
205                 return condition_test_security(c);
206
207         case CONDITION_CAPABILITY:
208                 return condition_test_capability(c);
209
210         case CONDITION_HOST:
211                 return condition_test_host(c);
212
213         case CONDITION_AC_POWER:
214                 return condition_test_ac_power(c);
215
216         case CONDITION_ARCHITECTURE:
217                 return condition_test_architecture(c);
218
219         case CONDITION_NEEDS_UPDATE:
220                 return condition_test_needs_update(c);
221
222         case CONDITION_FIRST_BOOT:
223                 return condition_test_first_boot(c);
224
225         case CONDITION_NULL:
226                 return !c->negate;
227
228         default:
229                 assert_not_reached("Invalid condition type.");
230         }
231 }
232
233 bool condition_test_list(const char *unit, Condition *first) {
234         Condition *c;
235         int triggered = -1;
236
237         /* If the condition list is empty, then it is true */
238         if (!first)
239                 return true;
240
241         /* Otherwise, if all of the non-trigger conditions apply and
242          * if any of the trigger conditions apply (unless there are
243          * none) we return true */
244         LIST_FOREACH(conditions, c, first) {
245                 bool b;
246
247                 b = condition_test(c);
248                 if (unit)
249                         log_debug_unit(unit,
250                                        "%s=%s%s%s %s for %s.",
251                                        condition_type_to_string(c->type),
252                                        c->trigger ? "|" : "",
253                                        c->negate ? "!" : "",
254                                        c->parameter,
255                                        b ? "succeeded" : "failed",
256                                        unit);
257                 c->state = b ? 1 : -1;
258
259                 if (!c->trigger && !b)
260                         return false;
261
262                 if (c->trigger && triggered <= 0)
263                         triggered = b;
264         }
265
266         return triggered != 0;
267 }