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