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