]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <linux/capability.h> | |
4 | ||
5 | #include "bus-common-errors.h" | |
6 | #include "bus-polkit.h" | |
7 | #include "fd-util.h" | |
8 | #include "format-util.h" | |
9 | #include "home-util.h" | |
10 | #include "homed-bus.h" | |
11 | #include "homed-home-bus.h" | |
12 | #include "homed-home.h" | |
13 | #include "strv.h" | |
14 | #include "user-record-util.h" | |
15 | #include "user-util.h" | |
16 | ||
17 | static int property_get_unix_record( | |
18 | sd_bus *bus, | |
19 | const char *path, | |
20 | const char *interface, | |
21 | const char *property, | |
22 | sd_bus_message *reply, | |
23 | void *userdata, | |
24 | sd_bus_error *error) { | |
25 | ||
26 | Home *h = ASSERT_PTR(userdata); | |
27 | ||
28 | assert(bus); | |
29 | assert(reply); | |
30 | ||
31 | return sd_bus_message_append( | |
32 | reply, "(suusss)", | |
33 | h->user_name, | |
34 | (uint32_t) h->uid, | |
35 | h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID, | |
36 | h->record ? user_record_real_name(h->record) : NULL, | |
37 | h->record ? user_record_home_directory(h->record) : NULL, | |
38 | h->record ? user_record_shell(h->record) : NULL); | |
39 | } | |
40 | ||
41 | static int property_get_state( | |
42 | sd_bus *bus, | |
43 | const char *path, | |
44 | const char *interface, | |
45 | const char *property, | |
46 | sd_bus_message *reply, | |
47 | void *userdata, | |
48 | sd_bus_error *error) { | |
49 | ||
50 | Home *h = ASSERT_PTR(userdata); | |
51 | ||
52 | assert(bus); | |
53 | assert(reply); | |
54 | ||
55 | return sd_bus_message_append(reply, "s", home_state_to_string(home_get_state(h))); | |
56 | } | |
57 | ||
58 | int bus_home_client_is_trusted(Home *h, sd_bus_message *message) { | |
59 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; | |
60 | uid_t euid; | |
61 | int r; | |
62 | ||
63 | assert(h); | |
64 | ||
65 | if (!message) | |
66 | return -EINVAL; | |
67 | ||
68 | r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); | |
69 | if (r < 0) | |
70 | return r; | |
71 | ||
72 | r = sd_bus_creds_get_euid(creds, &euid); | |
73 | if (r < 0) | |
74 | return r; | |
75 | ||
76 | return euid == 0 || h->uid == euid; | |
77 | } | |
78 | ||
79 | static int home_verify_polkit_async( | |
80 | Home *h, | |
81 | sd_bus_message *message, | |
82 | const char *action, | |
83 | uid_t good_uid, | |
84 | sd_bus_error *error) { | |
85 | ||
86 | assert(h); | |
87 | assert(message); | |
88 | assert(action); | |
89 | assert(error); | |
90 | ||
91 | const char *details[] = { | |
92 | "uid", FORMAT_UID(h->uid), | |
93 | "username", h->user_name, | |
94 | NULL | |
95 | }; | |
96 | ||
97 | return bus_verify_polkit_async_full( | |
98 | message, | |
99 | action, | |
100 | details, | |
101 | good_uid, | |
102 | /* flags= */ 0, | |
103 | &h->manager->polkit_registry, | |
104 | error); | |
105 | } | |
106 | ||
107 | int bus_home_get_record_json( | |
108 | Home *h, | |
109 | sd_bus_message *message, | |
110 | char **ret, | |
111 | bool *ret_incomplete) { | |
112 | ||
113 | _cleanup_(user_record_unrefp) UserRecord *augmented = NULL; | |
114 | UserRecordLoadFlags flags; | |
115 | int r, trusted; | |
116 | ||
117 | assert(h); | |
118 | assert(ret); | |
119 | ||
120 | trusted = bus_home_client_is_trusted(h, message); | |
121 | if (trusted < 0) { | |
122 | log_warning_errno(trusted, "Failed to determine whether client is trusted, assuming untrusted."); | |
123 | trusted = false; | |
124 | } | |
125 | ||
126 | flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE; | |
127 | if (trusted) | |
128 | flags |= USER_RECORD_ALLOW_PRIVILEGED; | |
129 | else | |
130 | flags |= USER_RECORD_STRIP_PRIVILEGED; | |
131 | ||
132 | r = home_augment_status(h, flags, &augmented); | |
133 | if (r < 0) | |
134 | return r; | |
135 | ||
136 | r = json_variant_format(augmented->json, 0, ret); | |
137 | if (r < 0) | |
138 | return r; | |
139 | ||
140 | if (ret_incomplete) | |
141 | *ret_incomplete = augmented->incomplete; | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int property_get_user_record( | |
147 | sd_bus *bus, | |
148 | const char *path, | |
149 | const char *interface, | |
150 | const char *property, | |
151 | sd_bus_message *reply, | |
152 | void *userdata, | |
153 | sd_bus_error *error) { | |
154 | ||
155 | _cleanup_free_ char *json = NULL; | |
156 | Home *h = ASSERT_PTR(userdata); | |
157 | bool incomplete; | |
158 | int r; | |
159 | ||
160 | assert(bus); | |
161 | assert(reply); | |
162 | ||
163 | r = bus_home_get_record_json(h, sd_bus_get_current_message(bus), &json, &incomplete); | |
164 | if (r < 0) | |
165 | return r; | |
166 | ||
167 | return sd_bus_message_append(reply, "(sb)", json, incomplete); | |
168 | } | |
169 | ||
170 | int bus_home_method_activate( | |
171 | sd_bus_message *message, | |
172 | void *userdata, | |
173 | sd_bus_error *error) { | |
174 | ||
175 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
176 | Home *h = ASSERT_PTR(userdata); | |
177 | bool if_referenced; | |
178 | int r; | |
179 | ||
180 | assert(message); | |
181 | ||
182 | if_referenced = endswith(sd_bus_message_get_member(message), "IfReferenced"); | |
183 | ||
184 | r = bus_verify_polkit_async_full( | |
185 | message, | |
186 | "org.freedesktop.home1.activate-home", | |
187 | /* details= */ NULL, | |
188 | h->uid, | |
189 | /* flags= */ 0, | |
190 | &h->manager->polkit_registry, | |
191 | error); | |
192 | if (r < 0) | |
193 | return r; | |
194 | if (r == 0) | |
195 | return 1; /* Will call us back */ | |
196 | ||
197 | r = bus_message_read_secret(message, &secret, error); | |
198 | if (r < 0) | |
199 | return r; | |
200 | ||
201 | r = home_activate(h, if_referenced, secret, error); | |
202 | if (r < 0) | |
203 | return r; | |
204 | ||
205 | assert(r == 0); | |
206 | assert(!h->current_operation); | |
207 | ||
208 | /* The operation is now in process, keep track of this message so that we can later reply to it. */ | |
209 | r = home_set_current_message(h, message); | |
210 | if (r < 0) | |
211 | return r; | |
212 | ||
213 | return 1; | |
214 | } | |
215 | ||
216 | int bus_home_method_deactivate( | |
217 | sd_bus_message *message, | |
218 | void *userdata, | |
219 | sd_bus_error *error) { | |
220 | ||
221 | Home *h = ASSERT_PTR(userdata); | |
222 | int r; | |
223 | ||
224 | assert(message); | |
225 | ||
226 | r = home_deactivate(h, false, error); | |
227 | if (r < 0) | |
228 | return r; | |
229 | ||
230 | assert(r == 0); | |
231 | assert(!h->current_operation); | |
232 | ||
233 | r = home_set_current_message(h, message); | |
234 | if (r < 0) | |
235 | return r; | |
236 | ||
237 | return 1; | |
238 | } | |
239 | ||
240 | int bus_home_method_unregister( | |
241 | sd_bus_message *message, | |
242 | void *userdata, | |
243 | sd_bus_error *error) { | |
244 | ||
245 | Home *h = ASSERT_PTR(userdata); | |
246 | int r; | |
247 | ||
248 | assert(message); | |
249 | ||
250 | r = home_verify_polkit_async( | |
251 | h, | |
252 | message, | |
253 | "org.freedesktop.home1.remove-home", | |
254 | UID_INVALID, | |
255 | error); | |
256 | if (r < 0) | |
257 | return r; | |
258 | if (r == 0) | |
259 | return 1; /* Will call us back */ | |
260 | ||
261 | r = home_unregister(h, error); | |
262 | if (r < 0) | |
263 | return r; | |
264 | ||
265 | assert(r > 0); | |
266 | ||
267 | /* Note that home_unregister() destroyed 'h' here, so no more accesses */ | |
268 | ||
269 | return sd_bus_reply_method_return(message, NULL); | |
270 | } | |
271 | ||
272 | int bus_home_method_realize( | |
273 | sd_bus_message *message, | |
274 | void *userdata, | |
275 | sd_bus_error *error) { | |
276 | ||
277 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
278 | Home *h = ASSERT_PTR(userdata); | |
279 | int r; | |
280 | ||
281 | assert(message); | |
282 | ||
283 | r = bus_message_read_secret(message, &secret, error); | |
284 | if (r < 0) | |
285 | return r; | |
286 | ||
287 | r = home_verify_polkit_async( | |
288 | h, | |
289 | message, | |
290 | "org.freedesktop.home1.create-home", | |
291 | UID_INVALID, | |
292 | error); | |
293 | if (r < 0) | |
294 | return r; | |
295 | if (r == 0) | |
296 | return 1; /* Will call us back */ | |
297 | ||
298 | r = home_create(h, secret, NULL, 0, error); | |
299 | if (r < 0) | |
300 | return r; | |
301 | ||
302 | assert(r == 0); | |
303 | assert(!h->current_operation); | |
304 | ||
305 | h->unregister_on_failure = false; | |
306 | ||
307 | r = home_set_current_message(h, message); | |
308 | if (r < 0) | |
309 | return r; | |
310 | ||
311 | return 1; | |
312 | } | |
313 | ||
314 | int bus_home_method_remove( | |
315 | sd_bus_message *message, | |
316 | void *userdata, | |
317 | sd_bus_error *error) { | |
318 | ||
319 | Home *h = ASSERT_PTR(userdata); | |
320 | int r; | |
321 | ||
322 | assert(message); | |
323 | ||
324 | r = home_verify_polkit_async( | |
325 | h, | |
326 | message, | |
327 | "org.freedesktop.home1.remove-home", | |
328 | UID_INVALID, | |
329 | error); | |
330 | if (r < 0) | |
331 | return r; | |
332 | if (r == 0) | |
333 | return 1; /* Will call us back */ | |
334 | ||
335 | r = home_remove(h, error); | |
336 | if (r < 0) | |
337 | return r; | |
338 | if (r > 0) /* Done already. Note that home_remove() destroyed 'h' here, so no more accesses */ | |
339 | return sd_bus_reply_method_return(message, NULL); | |
340 | ||
341 | assert(!h->current_operation); | |
342 | ||
343 | r = home_set_current_message(h, message); | |
344 | if (r < 0) | |
345 | return r; | |
346 | ||
347 | return 1; | |
348 | } | |
349 | ||
350 | int bus_home_method_fixate( | |
351 | sd_bus_message *message, | |
352 | void *userdata, | |
353 | sd_bus_error *error) { | |
354 | ||
355 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
356 | Home *h = ASSERT_PTR(userdata); | |
357 | int r; | |
358 | ||
359 | assert(message); | |
360 | ||
361 | r = bus_message_read_secret(message, &secret, error); | |
362 | if (r < 0) | |
363 | return r; | |
364 | ||
365 | r = home_fixate(h, secret, error); | |
366 | if (r < 0) | |
367 | return r; | |
368 | ||
369 | assert(r == 0); | |
370 | assert(!h->current_operation); | |
371 | ||
372 | r = home_set_current_message(h, message); | |
373 | if (r < 0) | |
374 | return r; | |
375 | ||
376 | return 1; | |
377 | } | |
378 | ||
379 | int bus_home_method_authenticate( | |
380 | sd_bus_message *message, | |
381 | void *userdata, | |
382 | sd_bus_error *error) { | |
383 | ||
384 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
385 | Home *h = ASSERT_PTR(userdata); | |
386 | int r; | |
387 | ||
388 | assert(message); | |
389 | ||
390 | r = bus_message_read_secret(message, &secret, error); | |
391 | if (r < 0) | |
392 | return r; | |
393 | ||
394 | r = home_verify_polkit_async( | |
395 | h, | |
396 | message, | |
397 | "org.freedesktop.home1.authenticate-home", | |
398 | h->uid, | |
399 | error); | |
400 | if (r < 0) | |
401 | return r; | |
402 | if (r == 0) | |
403 | return 1; /* Will call us back */ | |
404 | ||
405 | r = home_authenticate(h, secret, error); | |
406 | if (r < 0) | |
407 | return r; | |
408 | ||
409 | assert(r == 0); | |
410 | assert(!h->current_operation); | |
411 | ||
412 | r = home_set_current_message(h, message); | |
413 | if (r < 0) | |
414 | return r; | |
415 | ||
416 | return 1; | |
417 | } | |
418 | ||
419 | int bus_home_update_record( | |
420 | Home *h, | |
421 | sd_bus_message *message, | |
422 | UserRecord *hr, | |
423 | Hashmap *blobs, | |
424 | uint64_t flags, | |
425 | sd_bus_error *error) { | |
426 | int r; | |
427 | ||
428 | assert(h); | |
429 | assert(message); | |
430 | assert(hr); | |
431 | ||
432 | r = user_record_is_supported(hr, error); | |
433 | if (r < 0) | |
434 | return r; | |
435 | ||
436 | if ((flags & ~SD_HOMED_UPDATE_FLAGS_ALL) != 0) | |
437 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags provided."); | |
438 | ||
439 | r = home_verify_polkit_async( | |
440 | h, | |
441 | message, | |
442 | "org.freedesktop.home1.update-home", | |
443 | UID_INVALID, | |
444 | error); | |
445 | if (r < 0) | |
446 | return r; | |
447 | if (r == 0) | |
448 | return 1; /* Will call us back */ | |
449 | ||
450 | r = home_update(h, hr, blobs, flags, error); | |
451 | if (r < 0) | |
452 | return r; | |
453 | ||
454 | assert(r == 0); | |
455 | assert(!h->current_operation); | |
456 | ||
457 | r = home_set_current_message(h, message); | |
458 | if (r < 0) | |
459 | return r; | |
460 | ||
461 | h->current_operation->call_flags = flags; | |
462 | ||
463 | return 1; | |
464 | } | |
465 | ||
466 | int bus_home_method_update( | |
467 | sd_bus_message *message, | |
468 | void *userdata, | |
469 | sd_bus_error *error) { | |
470 | ||
471 | _cleanup_(user_record_unrefp) UserRecord *hr = NULL; | |
472 | _cleanup_hashmap_free_ Hashmap *blobs = NULL; | |
473 | uint64_t flags = 0; | |
474 | Home *h = ASSERT_PTR(userdata); | |
475 | int r; | |
476 | ||
477 | assert(message); | |
478 | ||
479 | r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE, &hr, error); | |
480 | if (r < 0) | |
481 | return r; | |
482 | ||
483 | if (endswith(sd_bus_message_get_member(message), "Ex")) { | |
484 | r = bus_message_read_blobs(message, &blobs, error); | |
485 | if (r < 0) | |
486 | return r; | |
487 | ||
488 | r = sd_bus_message_read(message, "t", &flags); | |
489 | if (r < 0) | |
490 | return r; | |
491 | } | |
492 | ||
493 | return bus_home_update_record(h, message, hr, blobs, flags, error); | |
494 | } | |
495 | ||
496 | int bus_home_method_resize( | |
497 | sd_bus_message *message, | |
498 | void *userdata, | |
499 | sd_bus_error *error) { | |
500 | ||
501 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
502 | Home *h = ASSERT_PTR(userdata); | |
503 | uint64_t sz; | |
504 | int r; | |
505 | ||
506 | assert(message); | |
507 | ||
508 | r = sd_bus_message_read(message, "t", &sz); | |
509 | if (r < 0) | |
510 | return r; | |
511 | ||
512 | r = bus_message_read_secret(message, &secret, error); | |
513 | if (r < 0) | |
514 | return r; | |
515 | ||
516 | r = home_verify_polkit_async( | |
517 | h, | |
518 | message, | |
519 | "org.freedesktop.home1.resize-home", | |
520 | UID_INVALID, | |
521 | error); | |
522 | if (r < 0) | |
523 | return r; | |
524 | if (r == 0) | |
525 | return 1; /* Will call us back */ | |
526 | ||
527 | r = home_resize(h, sz, secret, error); | |
528 | if (r < 0) | |
529 | return r; | |
530 | ||
531 | assert(r == 0); | |
532 | assert(!h->current_operation); | |
533 | ||
534 | r = home_set_current_message(h, message); | |
535 | if (r < 0) | |
536 | return r; | |
537 | ||
538 | return 1; | |
539 | } | |
540 | ||
541 | int bus_home_method_change_password( | |
542 | sd_bus_message *message, | |
543 | void *userdata, | |
544 | sd_bus_error *error) { | |
545 | ||
546 | _cleanup_(user_record_unrefp) UserRecord *new_secret = NULL, *old_secret = NULL; | |
547 | Home *h = ASSERT_PTR(userdata); | |
548 | int r; | |
549 | ||
550 | assert(message); | |
551 | ||
552 | r = bus_message_read_secret(message, &new_secret, error); | |
553 | if (r < 0) | |
554 | return r; | |
555 | ||
556 | r = bus_message_read_secret(message, &old_secret, error); | |
557 | if (r < 0) | |
558 | return r; | |
559 | ||
560 | r = home_verify_polkit_async( | |
561 | h, | |
562 | message, | |
563 | "org.freedesktop.home1.passwd-home", | |
564 | h->uid, | |
565 | error); | |
566 | if (r < 0) | |
567 | return r; | |
568 | if (r == 0) | |
569 | return 1; /* Will call us back */ | |
570 | ||
571 | r = home_passwd(h, new_secret, old_secret, error); | |
572 | if (r < 0) | |
573 | return r; | |
574 | ||
575 | assert(r == 0); | |
576 | assert(!h->current_operation); | |
577 | ||
578 | r = home_set_current_message(h, message); | |
579 | if (r < 0) | |
580 | return r; | |
581 | ||
582 | return 1; | |
583 | } | |
584 | ||
585 | int bus_home_method_lock( | |
586 | sd_bus_message *message, | |
587 | void *userdata, | |
588 | sd_bus_error *error) { | |
589 | ||
590 | Home *h = ASSERT_PTR(userdata); | |
591 | int r; | |
592 | ||
593 | assert(message); | |
594 | ||
595 | r = home_lock(h, error); | |
596 | if (r < 0) | |
597 | return r; | |
598 | if (r > 0) /* Done */ | |
599 | return sd_bus_reply_method_return(message, NULL); | |
600 | ||
601 | /* The operation is now in process, keep track of this message so that we can later reply to it. */ | |
602 | assert(!h->current_operation); | |
603 | ||
604 | r = home_set_current_message(h, message); | |
605 | if (r < 0) | |
606 | return r; | |
607 | ||
608 | return 1; | |
609 | } | |
610 | ||
611 | int bus_home_method_unlock( | |
612 | sd_bus_message *message, | |
613 | void *userdata, | |
614 | sd_bus_error *error) { | |
615 | ||
616 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
617 | Home *h = ASSERT_PTR(userdata); | |
618 | int r; | |
619 | ||
620 | assert(message); | |
621 | ||
622 | r = bus_message_read_secret(message, &secret, error); | |
623 | if (r < 0) | |
624 | return r; | |
625 | ||
626 | r = home_unlock(h, secret, error); | |
627 | if (r < 0) | |
628 | return r; | |
629 | ||
630 | assert(r == 0); | |
631 | assert(!h->current_operation); | |
632 | ||
633 | /* The operation is now in process, keep track of this message so that we can later reply to it. */ | |
634 | r = home_set_current_message(h, message); | |
635 | if (r < 0) | |
636 | return r; | |
637 | ||
638 | return 1; | |
639 | } | |
640 | ||
641 | int bus_home_method_acquire( | |
642 | sd_bus_message *message, | |
643 | void *userdata, | |
644 | sd_bus_error *error) { | |
645 | ||
646 | _cleanup_(user_record_unrefp) UserRecord *secret = NULL; | |
647 | _cleanup_(operation_unrefp) Operation *o = NULL; | |
648 | _cleanup_close_ int fd = -EBADF; | |
649 | int r, please_suspend; | |
650 | Home *h = ASSERT_PTR(userdata); | |
651 | ||
652 | assert(message); | |
653 | ||
654 | r = bus_message_read_secret(message, &secret, error); | |
655 | if (r < 0) | |
656 | return r; | |
657 | ||
658 | r = sd_bus_message_read(message, "b", &please_suspend); | |
659 | if (r < 0) | |
660 | return r; | |
661 | ||
662 | /* This operation might not be something we can executed immediately, hence queue it */ | |
663 | fd = home_create_fifo(h, please_suspend); | |
664 | if (fd < 0) | |
665 | return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name); | |
666 | ||
667 | o = operation_new(OPERATION_ACQUIRE, message); | |
668 | if (!o) | |
669 | return -ENOMEM; | |
670 | ||
671 | o->secret = TAKE_PTR(secret); | |
672 | o->send_fd = TAKE_FD(fd); | |
673 | ||
674 | r = home_schedule_operation(h, o, error); | |
675 | if (r < 0) | |
676 | return r; | |
677 | ||
678 | return 1; | |
679 | } | |
680 | ||
681 | int bus_home_method_ref( | |
682 | sd_bus_message *message, | |
683 | void *userdata, | |
684 | sd_bus_error *error) { | |
685 | ||
686 | _cleanup_close_ int fd = -EBADF; | |
687 | Home *h = ASSERT_PTR(userdata); | |
688 | int please_suspend, r; | |
689 | bool unrestricted; | |
690 | ||
691 | assert(message); | |
692 | ||
693 | /* In unrestricted mode we'll add a reference to the home even if it's not active */ | |
694 | unrestricted = strstr(sd_bus_message_get_member(message), "Unrestricted"); | |
695 | ||
696 | r = sd_bus_message_read(message, "b", &please_suspend); | |
697 | if (r < 0) | |
698 | return r; | |
699 | ||
700 | if (!unrestricted) { | |
701 | HomeState state; | |
702 | ||
703 | state = home_get_state(h); | |
704 | ||
705 | switch (state) { | |
706 | case HOME_ABSENT: | |
707 | return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name); | |
708 | case HOME_UNFIXATED: | |
709 | case HOME_INACTIVE: | |
710 | case HOME_DIRTY: | |
711 | return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name); | |
712 | case HOME_LOCKED: | |
713 | return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name); | |
714 | default: | |
715 | if (HOME_STATE_IS_ACTIVE(state)) | |
716 | break; | |
717 | ||
718 | return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name); | |
719 | } | |
720 | } | |
721 | ||
722 | fd = home_create_fifo(h, please_suspend); | |
723 | if (fd < 0) | |
724 | return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name); | |
725 | ||
726 | return sd_bus_reply_method_return(message, "h", fd); | |
727 | } | |
728 | ||
729 | int bus_home_method_release( | |
730 | sd_bus_message *message, | |
731 | void *userdata, | |
732 | sd_bus_error *error) { | |
733 | ||
734 | _cleanup_(operation_unrefp) Operation *o = NULL; | |
735 | Home *h = ASSERT_PTR(userdata); | |
736 | int r; | |
737 | ||
738 | assert(message); | |
739 | ||
740 | o = operation_new(OPERATION_RELEASE, message); | |
741 | if (!o) | |
742 | return -ENOMEM; | |
743 | ||
744 | r = home_schedule_operation(h, o, error); | |
745 | if (r < 0) | |
746 | return r; | |
747 | ||
748 | return 1; | |
749 | } | |
750 | ||
751 | /* We map a uid_t as uint32_t bus property, let's ensure this is safe. */ | |
752 | assert_cc(sizeof(uid_t) == sizeof(uint32_t)); | |
753 | ||
754 | int bus_home_path(Home *h, char **ret) { | |
755 | assert(ret); | |
756 | ||
757 | return sd_bus_path_encode("/org/freedesktop/home1/home", h->user_name, ret); | |
758 | } | |
759 | ||
760 | static int bus_home_object_find( | |
761 | sd_bus *bus, | |
762 | const char *path, | |
763 | const char *interface, | |
764 | void *userdata, | |
765 | void **found, | |
766 | sd_bus_error *error) { | |
767 | ||
768 | _cleanup_free_ char *e = NULL; | |
769 | Manager *m = userdata; | |
770 | uid_t uid; | |
771 | Home *h; | |
772 | int r; | |
773 | ||
774 | r = sd_bus_path_decode(path, "/org/freedesktop/home1/home", &e); | |
775 | if (r <= 0) | |
776 | return 0; | |
777 | ||
778 | if (parse_uid(e, &uid) >= 0) | |
779 | h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid)); | |
780 | else | |
781 | h = hashmap_get(m->homes_by_name, e); | |
782 | if (!h) | |
783 | return 0; | |
784 | ||
785 | *found = h; | |
786 | return 1; | |
787 | } | |
788 | ||
789 | static int bus_home_node_enumerator( | |
790 | sd_bus *bus, | |
791 | const char *path, | |
792 | void *userdata, | |
793 | char ***nodes, | |
794 | sd_bus_error *error) { | |
795 | ||
796 | _cleanup_strv_free_ char **l = NULL; | |
797 | Manager *m = userdata; | |
798 | size_t k = 0; | |
799 | Home *h; | |
800 | int r; | |
801 | ||
802 | assert(nodes); | |
803 | ||
804 | l = new0(char*, hashmap_size(m->homes_by_uid) + 1); | |
805 | if (!l) | |
806 | return -ENOMEM; | |
807 | ||
808 | HASHMAP_FOREACH(h, m->homes_by_uid) { | |
809 | r = bus_home_path(h, l + k); | |
810 | if (r < 0) | |
811 | return r; | |
812 | ||
813 | k++; | |
814 | } | |
815 | ||
816 | *nodes = TAKE_PTR(l); | |
817 | return 1; | |
818 | } | |
819 | ||
820 | const sd_bus_vtable home_vtable[] = { | |
821 | SD_BUS_VTABLE_START(0), | |
822 | ||
823 | SD_BUS_PROPERTY("UserName", "s", | |
824 | NULL, offsetof(Home, user_name), | |
825 | SD_BUS_VTABLE_PROPERTY_CONST), | |
826 | SD_BUS_PROPERTY("UID", "u", | |
827 | NULL, offsetof(Home, uid), | |
828 | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
829 | SD_BUS_PROPERTY("UnixRecord", "(suusss)", | |
830 | property_get_unix_record, 0, | |
831 | SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | |
832 | SD_BUS_PROPERTY("State", "s", | |
833 | property_get_state, 0, | |
834 | 0), | |
835 | SD_BUS_PROPERTY("UserRecord", "(sb)", | |
836 | property_get_user_record, 0, | |
837 | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE), | |
838 | ||
839 | SD_BUS_METHOD_WITH_ARGS("Activate", | |
840 | SD_BUS_ARGS("s", secret), | |
841 | SD_BUS_NO_RESULT, | |
842 | bus_home_method_activate, | |
843 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
844 | SD_BUS_METHOD_WITH_ARGS("ActivateIfReferenced", | |
845 | SD_BUS_ARGS("s", secret), | |
846 | SD_BUS_NO_RESULT, | |
847 | bus_home_method_activate, | |
848 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
849 | SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0), | |
850 | SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED), | |
851 | SD_BUS_METHOD_WITH_ARGS("Realize", | |
852 | SD_BUS_ARGS("s", secret), | |
853 | SD_BUS_NO_RESULT, | |
854 | bus_home_method_realize, | |
855 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
856 | ||
857 | SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), | |
858 | SD_BUS_METHOD_WITH_ARGS("Fixate", | |
859 | SD_BUS_ARGS("s", secret), | |
860 | SD_BUS_NO_RESULT, | |
861 | bus_home_method_fixate, | |
862 | SD_BUS_VTABLE_SENSITIVE), | |
863 | SD_BUS_METHOD_WITH_ARGS("Authenticate", | |
864 | SD_BUS_ARGS("s", secret), | |
865 | SD_BUS_NO_RESULT, | |
866 | bus_home_method_authenticate, | |
867 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
868 | SD_BUS_METHOD_WITH_ARGS("Update", | |
869 | SD_BUS_ARGS("s", user_record), | |
870 | SD_BUS_NO_RESULT, | |
871 | bus_home_method_update, | |
872 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
873 | SD_BUS_METHOD_WITH_ARGS("UpdateEx", | |
874 | SD_BUS_ARGS("s", user_record, "a{sh}", blobs, "t", flags), | |
875 | SD_BUS_NO_RESULT, | |
876 | bus_home_method_update, | |
877 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
878 | SD_BUS_METHOD_WITH_ARGS("Resize", | |
879 | SD_BUS_ARGS("t", size, "s", secret), | |
880 | SD_BUS_NO_RESULT, | |
881 | bus_home_method_resize, | |
882 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
883 | SD_BUS_METHOD_WITH_ARGS("ChangePassword", | |
884 | SD_BUS_ARGS("s", new_secret, "s", old_secret), | |
885 | SD_BUS_NO_RESULT, | |
886 | bus_home_method_change_password, | |
887 | SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), | |
888 | SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0), | |
889 | SD_BUS_METHOD_WITH_ARGS("Unlock", | |
890 | SD_BUS_ARGS("s", secret), | |
891 | SD_BUS_NO_RESULT, | |
892 | bus_home_method_unlock, | |
893 | SD_BUS_VTABLE_SENSITIVE), | |
894 | SD_BUS_METHOD_WITH_ARGS("Acquire", | |
895 | SD_BUS_ARGS("s", secret, "b", please_suspend), | |
896 | SD_BUS_RESULT("h", send_fd), | |
897 | bus_home_method_acquire, | |
898 | SD_BUS_VTABLE_SENSITIVE), | |
899 | SD_BUS_METHOD_WITH_ARGS("Ref", | |
900 | SD_BUS_ARGS("b", please_suspend), | |
901 | SD_BUS_RESULT("h", send_fd), | |
902 | bus_home_method_ref, | |
903 | 0), | |
904 | SD_BUS_METHOD_WITH_ARGS("RefUnrestricted", | |
905 | SD_BUS_ARGS("b", please_suspend), | |
906 | SD_BUS_RESULT("h", send_fd), | |
907 | bus_home_method_ref, | |
908 | 0), | |
909 | SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0), | |
910 | SD_BUS_VTABLE_END | |
911 | }; | |
912 | ||
913 | const BusObjectImplementation home_object = { | |
914 | "/org/freedesktop/home1/home", | |
915 | "org.freedesktop.home1.Home", | |
916 | .fallback_vtables = BUS_FALLBACK_VTABLES({home_vtable, bus_home_object_find}), | |
917 | .node_enumerator = bus_home_node_enumerator, | |
918 | .manager = true, | |
919 | }; | |
920 | ||
921 | static int on_deferred_change(sd_event_source *s, void *userdata) { | |
922 | _cleanup_free_ char *path = NULL; | |
923 | Home *h = ASSERT_PTR(userdata); | |
924 | int r; | |
925 | ||
926 | h->deferred_change_event_source = sd_event_source_disable_unref(h->deferred_change_event_source); | |
927 | ||
928 | r = bus_home_path(h, &path); | |
929 | if (r < 0) { | |
930 | log_warning_errno(r, "Failed to generate home bus path, ignoring: %m"); | |
931 | return 0; | |
932 | } | |
933 | ||
934 | if (h->announced) | |
935 | r = sd_bus_emit_properties_changed_strv(h->manager->bus, path, "org.freedesktop.home1.Home", NULL); | |
936 | else | |
937 | r = sd_bus_emit_object_added(h->manager->bus, path); | |
938 | if (r < 0) | |
939 | log_warning_errno(r, "Failed to send home change event, ignoring: %m"); | |
940 | else | |
941 | h->announced = true; | |
942 | ||
943 | return 0; | |
944 | } | |
945 | ||
946 | int bus_home_emit_change(Home *h) { | |
947 | int r; | |
948 | ||
949 | assert(h); | |
950 | ||
951 | if (h->deferred_change_event_source) | |
952 | return 1; | |
953 | ||
954 | if (!h->manager->event) | |
955 | return 0; | |
956 | ||
957 | if (IN_SET(sd_event_get_state(h->manager->event), SD_EVENT_FINISHED, SD_EVENT_EXITING)) | |
958 | return 0; | |
959 | ||
960 | r = sd_event_add_defer(h->manager->event, &h->deferred_change_event_source, on_deferred_change, h); | |
961 | if (r < 0) | |
962 | return log_error_errno(r, "Failed to allocate deferred change event source: %m"); | |
963 | ||
964 | r = sd_event_source_set_priority(h->deferred_change_event_source, SD_EVENT_PRIORITY_IDLE+5); | |
965 | if (r < 0) | |
966 | log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m"); | |
967 | ||
968 | (void) sd_event_source_set_description(h->deferred_change_event_source, "deferred-change-event"); | |
969 | return 1; | |
970 | } | |
971 | ||
972 | int bus_home_emit_remove(Home *h) { | |
973 | _cleanup_free_ char *path = NULL; | |
974 | int r; | |
975 | ||
976 | assert(h); | |
977 | ||
978 | if (!h->announced) | |
979 | return 0; | |
980 | ||
981 | if (!h->manager) | |
982 | return 0; | |
983 | ||
984 | if (!h->manager->bus) | |
985 | return 0; | |
986 | ||
987 | r = bus_home_path(h, &path); | |
988 | if (r < 0) | |
989 | return r; | |
990 | ||
991 | r = sd_bus_emit_object_removed(h->manager->bus, path); | |
992 | if (r < 0) | |
993 | return r; | |
994 | ||
995 | h->announced = false; | |
996 | return 1; | |
997 | } |