1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "bus-internal.h"
4 #include "bus-message.h"
5 #include "bus-polkit.h"
6 #include "bus-util.h"
7 #include "strv.h"
8 #include "user-util.h"
9 
check_good_user(sd_bus_message * m,uid_t good_user)10 static int check_good_user(sd_bus_message *m, uid_t good_user) {
11         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
12         uid_t sender_uid;
13         int r;
14 
15         assert(m);
16 
17         if (good_user == UID_INVALID)
18                 return 0;
19 
20         r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
21         if (r < 0)
22                 return r;
23 
24         /* Don't trust augmented credentials for authorization */
25         assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
26 
27         r = sd_bus_creds_get_euid(creds, &sender_uid);
28         if (r < 0)
29                 return r;
30 
31         return sender_uid == good_user;
32 }
33 
34 #if ENABLE_POLKIT
bus_message_append_strv_key_value(sd_bus_message * m,const char ** l)35 static int bus_message_append_strv_key_value(
36                 sd_bus_message *m,
37                 const char **l) {
38 
39         int r;
40 
41         assert(m);
42 
43         r = sd_bus_message_open_container(m, 'a', "{ss}");
44         if (r < 0)
45                 return r;
46 
47         STRV_FOREACH_PAIR(k, v, l) {
48                 r = sd_bus_message_append(m, "{ss}", *k, *v);
49                 if (r < 0)
50                         return r;
51         }
52 
53         r = sd_bus_message_close_container(m);
54         if (r < 0)
55                 return r;
56 
57         return r;
58 }
59 #endif
60 
bus_test_polkit(sd_bus_message * call,int capability,const char * action,const char ** details,uid_t good_user,bool * _challenge,sd_bus_error * ret_error)61 int bus_test_polkit(
62                 sd_bus_message *call,
63                 int capability,
64                 const char *action,
65                 const char **details,
66                 uid_t good_user,
67                 bool *_challenge,
68                 sd_bus_error *ret_error) {
69 
70         int r;
71 
72         assert(call);
73         assert(action);
74 
75         /* Tests non-interactively! */
76 
77         r = check_good_user(call, good_user);
78         if (r != 0)
79                 return r;
80 
81         r = sd_bus_query_sender_privilege(call, capability);
82         if (r < 0)
83                 return r;
84         else if (r > 0)
85                 return 1;
86 #if ENABLE_POLKIT
87         else {
88                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
89                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
90                 int authorized = false, challenge = false;
91                 const char *sender;
92 
93                 sender = sd_bus_message_get_sender(call);
94                 if (!sender)
95                         return -EBADMSG;
96 
97                 r = sd_bus_message_new_method_call(
98                                 call->bus,
99                                 &request,
100                                 "org.freedesktop.PolicyKit1",
101                                 "/org/freedesktop/PolicyKit1/Authority",
102                                 "org.freedesktop.PolicyKit1.Authority",
103                                 "CheckAuthorization");
104                 if (r < 0)
105                         return r;
106 
107                 r = sd_bus_message_append(
108                                 request,
109                                 "(sa{sv})s",
110                                 "system-bus-name", 1, "name", "s", sender,
111                                 action);
112                 if (r < 0)
113                         return r;
114 
115                 r = bus_message_append_strv_key_value(request, details);
116                 if (r < 0)
117                         return r;
118 
119                 r = sd_bus_message_append(request, "us", 0, NULL);
120                 if (r < 0)
121                         return r;
122 
123                 r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
124                 if (r < 0) {
125                         /* Treat no PK available as access denied */
126                         if (bus_error_is_unknown_service(ret_error)) {
127                                 sd_bus_error_free(ret_error);
128                                 return -EACCES;
129                         }
130 
131                         return r;
132                 }
133 
134                 r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
135                 if (r < 0)
136                         return r;
137 
138                 r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
139                 if (r < 0)
140                         return r;
141 
142                 if (authorized)
143                         return 1;
144 
145                 if (_challenge) {
146                         *_challenge = challenge;
147                         return 0;
148                 }
149         }
150 #endif
151 
152         return -EACCES;
153 }
154 
155 #if ENABLE_POLKIT
156 
157 typedef struct AsyncPolkitQuery {
158         char *action;
159         char **details;
160 
161         sd_bus_message *request, *reply;
162         sd_bus_slot *slot;
163 
164         Hashmap *registry;
165         sd_event_source *defer_event_source;
166 } AsyncPolkitQuery;
167 
async_polkit_query_free(AsyncPolkitQuery * q)168 static void async_polkit_query_free(AsyncPolkitQuery *q) {
169         if (!q)
170                 return;
171 
172         sd_bus_slot_unref(q->slot);
173 
174         if (q->registry && q->request)
175                 hashmap_remove(q->registry, q->request);
176 
177         sd_bus_message_unref(q->request);
178         sd_bus_message_unref(q->reply);
179 
180         free(q->action);
181         strv_free(q->details);
182 
183         sd_event_source_disable_unref(q->defer_event_source);
184         free(q);
185 }
186 
async_polkit_defer(sd_event_source * s,void * userdata)187 static int async_polkit_defer(sd_event_source *s, void *userdata) {
188         AsyncPolkitQuery *q = userdata;
189 
190         assert(s);
191 
192         /* This is called as idle event source after we processed the async polkit reply, hopefully after the
193          * method call we re-enqueued has been properly processed. */
194 
195         async_polkit_query_free(q);
196         return 0;
197 }
198 
async_polkit_callback(sd_bus_message * reply,void * userdata,sd_bus_error * error)199 static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
200         AsyncPolkitQuery *q = userdata;
201         int r;
202 
203         assert(reply);
204         assert(q);
205 
206         assert(q->slot);
207         q->slot = sd_bus_slot_unref(q->slot);
208 
209         assert(!q->reply);
210         q->reply = sd_bus_message_ref(reply);
211 
212         /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
213          * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
214          * again.
215          *
216          * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
217          * i.e. after the second time the message is processed is complete. */
218 
219         assert(!q->defer_event_source);
220         r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
221         if (r < 0)
222                 goto fail;
223 
224         r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
225         if (r < 0)
226                 goto fail;
227 
228         r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
229         if (r < 0)
230                 goto fail;
231 
232         r = sd_bus_message_rewind(q->request, true);
233         if (r < 0)
234                 goto fail;
235 
236         r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
237         if (r < 0)
238                 goto fail;
239 
240         return 1;
241 
242 fail:
243         log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
244         (void) sd_bus_reply_method_errno(q->request, r, NULL);
245         async_polkit_query_free(q);
246         return r;
247 }
248 
249 #endif
250 
bus_verify_polkit_async(sd_bus_message * call,int capability,const char * action,const char ** details,bool interactive,uid_t good_user,Hashmap ** registry,sd_bus_error * ret_error)251 int bus_verify_polkit_async(
252                 sd_bus_message *call,
253                 int capability,
254                 const char *action,
255                 const char **details,
256                 bool interactive,
257                 uid_t good_user,
258                 Hashmap **registry,
259                 sd_bus_error *ret_error) {
260 
261         const char *sender;
262         int r;
263 
264         assert(call);
265         assert(action);
266         assert(registry);
267 
268         r = check_good_user(call, good_user);
269         if (r != 0)
270                 return r;
271 
272 #if ENABLE_POLKIT
273         AsyncPolkitQuery *q = hashmap_get(*registry, call);
274         if (q) {
275                 int authorized, challenge;
276 
277                 /* This is the second invocation of this function, and there's already a response from
278                  * polkit, let's process it */
279                 assert(q->reply);
280 
281                 /* If the operation we want to authenticate changed between the first and the second time,
282                  * let's not use this authentication, it might be out of date as the object and context we
283                  * operate on might have changed. */
284                 if (!streq(q->action, action) ||
285                     !strv_equal(q->details, (char**) details))
286                         return -ESTALE;
287 
288                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
289                         const sd_bus_error *e;
290 
291                         e = sd_bus_message_get_error(q->reply);
292 
293                         /* Treat no PK available as access denied */
294                         if (bus_error_is_unknown_service(e))
295                                 return -EACCES;
296 
297                         /* Copy error from polkit reply */
298                         sd_bus_error_copy(ret_error, e);
299                         return -sd_bus_error_get_errno(e);
300                 }
301 
302                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
303                 if (r >= 0)
304                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
305                 if (r < 0)
306                         return r;
307 
308                 if (authorized)
309                         return 1;
310 
311                 if (challenge)
312                         return sd_bus_error_set(ret_error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
313 
314                 return -EACCES;
315         }
316 #endif
317 
318         r = sd_bus_query_sender_privilege(call, capability);
319         if (r < 0)
320                 return r;
321         else if (r > 0)
322                 return 1;
323 
324         sender = sd_bus_message_get_sender(call);
325         if (!sender)
326                 return -EBADMSG;
327 
328 #if ENABLE_POLKIT
329         _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
330 
331         int c = sd_bus_message_get_allow_interactive_authorization(call);
332         if (c < 0)
333                 return c;
334         if (c > 0)
335                 interactive = true;
336 
337         r = hashmap_ensure_allocated(registry, NULL);
338         if (r < 0)
339                 return r;
340 
341         r = sd_bus_message_new_method_call(
342                         call->bus,
343                         &pk,
344                         "org.freedesktop.PolicyKit1",
345                         "/org/freedesktop/PolicyKit1/Authority",
346                         "org.freedesktop.PolicyKit1.Authority",
347                         "CheckAuthorization");
348         if (r < 0)
349                 return r;
350 
351         r = sd_bus_message_append(
352                         pk,
353                         "(sa{sv})s",
354                         "system-bus-name", 1, "name", "s", sender,
355                         action);
356         if (r < 0)
357                 return r;
358 
359         r = bus_message_append_strv_key_value(pk, details);
360         if (r < 0)
361                 return r;
362 
363         r = sd_bus_message_append(pk, "us", interactive, NULL);
364         if (r < 0)
365                 return r;
366 
367         q = new(AsyncPolkitQuery, 1);
368         if (!q)
369                 return -ENOMEM;
370 
371         *q = (AsyncPolkitQuery) {
372                 .request = sd_bus_message_ref(call),
373         };
374 
375         q->action = strdup(action);
376         if (!q->action) {
377                 async_polkit_query_free(q);
378                 return -ENOMEM;
379         }
380 
381         q->details = strv_copy((char**) details);
382         if (!q->details) {
383                 async_polkit_query_free(q);
384                 return -ENOMEM;
385         }
386 
387         r = hashmap_put(*registry, call, q);
388         if (r < 0) {
389                 async_polkit_query_free(q);
390                 return r;
391         }
392 
393         q->registry = *registry;
394 
395         r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
396         if (r < 0) {
397                 async_polkit_query_free(q);
398                 return r;
399         }
400 
401         return 0;
402 #endif
403 
404         return -EACCES;
405 }
406 
bus_verify_polkit_async_registry_free(Hashmap * registry)407 Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
408 #if ENABLE_POLKIT
409         return hashmap_free_with_destructor(registry, async_polkit_query_free);
410 #else
411         assert(hashmap_isempty(registry));
412         return hashmap_free(registry);
413 #endif
414 }
415