]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homed-manager-bus.c
test: also flush and rotate journal before read
[thirdparty/systemd.git] / src / home / homed-manager-bus.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
70a5db58
LP
2
3#include <linux/capability.h>
4
5#include "alloc-util.h"
6#include "bus-common-errors.h"
7#include "bus-polkit.h"
8#include "format-util.h"
d94c7eef 9#include "home-util.h"
70a5db58
LP
10#include "homed-bus.h"
11#include "homed-home-bus.h"
12#include "homed-manager-bus.h"
13#include "homed-manager.h"
14#include "strv.h"
15#include "user-record-sign.h"
16#include "user-record-util.h"
17#include "user-util.h"
18
19static int property_get_auto_login(
20 sd_bus *bus,
21 const char *path,
22 const char *interface,
23 const char *property,
24 sd_bus_message *reply,
25 void *userdata,
26 sd_bus_error *error) {
27
99534007 28 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
29 Home *h;
30 int r;
31
32 assert(bus);
33 assert(reply);
70a5db58
LP
34
35 r = sd_bus_message_open_container(reply, 'a', "(sso)");
36 if (r < 0)
37 return r;
38
90e74a66 39 HASHMAP_FOREACH(h, m->homes_by_name) {
5d2a48da 40 _cleanup_strv_free_ char **seats = NULL;
70a5db58 41 _cleanup_free_ char *home_path = NULL;
70a5db58
LP
42
43 r = home_auto_login(h, &seats);
44 if (r < 0) {
45 log_debug_errno(r, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h->user_name);
46 continue;
47 }
48 if (!r)
49 continue;
50
51 r = bus_home_path(h, &home_path);
52 if (r < 0)
53 return log_error_errno(r, "Failed to generate home bus path: %m");
54
55 STRV_FOREACH(s, seats) {
56 r = sd_bus_message_append(reply, "(sso)", h->user_name, *s, home_path);
57 if (r < 0)
58 return r;
59 }
60 }
61
62 return sd_bus_message_close_container(reply);
63}
64
0e80e355
LP
65static int lookup_user_name(
66 Manager *m,
67 sd_bus_message *message,
68 const char *user_name,
69 sd_bus_error *error,
70 Home **ret) {
71
72 Home *h;
73 int r;
74
75 assert(m);
76 assert(message);
77 assert(user_name);
78 assert(ret);
79
80 if (isempty(user_name)) {
81 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
82 uid_t uid;
83
84 /* If an empty user name is specified, then identify caller's EUID and find home by that. */
85
86 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
87 if (r < 0)
88 return r;
89
90 r = sd_bus_creds_get_euid(creds, &uid);
91 if (r < 0)
92 return r;
93
94 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
95 if (!h)
96 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "Client's UID " UID_FMT " not managed.", uid);
97
98 } else {
99
100 if (!valid_user_group_name(user_name, 0))
101 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
102
103 h = hashmap_get(m->homes_by_name, user_name);
104 if (!h)
105 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
106 }
107
108 *ret = h;
109 return 0;
110}
111
70a5db58
LP
112static int method_get_home_by_name(
113 sd_bus_message *message,
114 void *userdata,
115 sd_bus_error *error) {
116
117 _cleanup_free_ char *path = NULL;
118 const char *user_name;
99534007 119 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
120 Home *h;
121 int r;
122
123 assert(message);
70a5db58
LP
124
125 r = sd_bus_message_read(message, "s", &user_name);
126 if (r < 0)
127 return r;
70a5db58 128
0e80e355
LP
129 r = lookup_user_name(m, message, user_name, error, &h);
130 if (r < 0)
131 return r;
70a5db58
LP
132
133 r = bus_home_path(h, &path);
134 if (r < 0)
135 return r;
136
137 return sd_bus_reply_method_return(
138 message, "usussso",
139 (uint32_t) h->uid,
140 home_state_to_string(home_get_state(h)),
141 h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
142 h->record ? user_record_real_name(h->record) : NULL,
143 h->record ? user_record_home_directory(h->record) : NULL,
144 h->record ? user_record_shell(h->record) : NULL,
145 path);
146}
147
148static int method_get_home_by_uid(
149 sd_bus_message *message,
150 void *userdata,
151 sd_bus_error *error) {
152
153 _cleanup_free_ char *path = NULL;
99534007 154 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
155 uint32_t uid;
156 int r;
157 Home *h;
158
159 assert(message);
70a5db58
LP
160
161 r = sd_bus_message_read(message, "u", &uid);
162 if (r < 0)
163 return r;
164 if (!uid_is_valid(uid))
165 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
166
167 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
168 if (!h)
169 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
170
171 /* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
172 * for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
173 * typical bus path by name. */
174 if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
175 return -ENOMEM;
176
177 return sd_bus_reply_method_return(
178 message, "ssussso",
179 h->user_name,
180 home_state_to_string(home_get_state(h)),
181 h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
182 h->record ? user_record_real_name(h->record) : NULL,
183 h->record ? user_record_home_directory(h->record) : NULL,
184 h->record ? user_record_shell(h->record) : NULL,
185 path);
186}
187
188static int method_list_homes(
189 sd_bus_message *message,
190 void *userdata,
191 sd_bus_error *error) {
192
193 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
99534007 194 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
195 Home *h;
196 int r;
197
198 assert(message);
70a5db58
LP
199
200 r = sd_bus_message_new_method_return(message, &reply);
201 if (r < 0)
202 return r;
203
204 r = sd_bus_message_open_container(reply, 'a', "(susussso)");
205 if (r < 0)
206 return r;
207
90e74a66 208 HASHMAP_FOREACH(h, m->homes_by_uid) {
70a5db58
LP
209 _cleanup_free_ char *path = NULL;
210
211 r = bus_home_path(h, &path);
212 if (r < 0)
213 return r;
214
215 r = sd_bus_message_append(
216 reply, "(susussso)",
217 h->user_name,
218 (uint32_t) h->uid,
219 home_state_to_string(home_get_state(h)),
220 h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
221 h->record ? user_record_real_name(h->record) : NULL,
222 h->record ? user_record_home_directory(h->record) : NULL,
223 h->record ? user_record_shell(h->record) : NULL,
224 path);
225 if (r < 0)
226 return r;
227 }
228
229 r = sd_bus_message_close_container(reply);
230 if (r < 0)
231 return r;
232
233 return sd_bus_send(NULL, reply, NULL);
234}
235
236static int method_get_user_record_by_name(
237 sd_bus_message *message,
238 void *userdata,
239 sd_bus_error *error) {
240
241 _cleanup_free_ char *json = NULL, *path = NULL;
99534007 242 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
243 const char *user_name;
244 bool incomplete;
245 Home *h;
246 int r;
247
248 assert(message);
70a5db58
LP
249
250 r = sd_bus_message_read(message, "s", &user_name);
251 if (r < 0)
252 return r;
70a5db58 253
0e80e355
LP
254 r = lookup_user_name(m, message, user_name, error, &h);
255 if (r < 0)
256 return r;
70a5db58
LP
257
258 r = bus_home_get_record_json(h, message, &json, &incomplete);
259 if (r < 0)
260 return r;
261
262 r = bus_home_path(h, &path);
263 if (r < 0)
264 return r;
265
266 return sd_bus_reply_method_return(
267 message, "sbo",
268 json,
269 incomplete,
270 path);
271}
272
273static int method_get_user_record_by_uid(
274 sd_bus_message *message,
275 void *userdata,
276 sd_bus_error *error) {
277
278 _cleanup_free_ char *json = NULL, *path = NULL;
99534007 279 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
280 bool incomplete;
281 uint32_t uid;
282 Home *h;
283 int r;
284
285 assert(message);
70a5db58
LP
286
287 r = sd_bus_message_read(message, "u", &uid);
288 if (r < 0)
289 return r;
290 if (!uid_is_valid(uid))
291 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
292
293 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
294 if (!h)
295 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
296
297 r = bus_home_get_record_json(h, message, &json, &incomplete);
298 if (r < 0)
299 return r;
300
301 if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
302 return -ENOMEM;
303
304 return sd_bus_reply_method_return(
305 message, "sbo",
306 json,
307 incomplete,
308 path);
309}
310
311static int generic_home_method(
312 Manager *m,
313 sd_bus_message *message,
314 sd_bus_message_handler_t handler,
315 sd_bus_error *error) {
316
317 const char *user_name;
318 Home *h;
319 int r;
320
faef9ba2
LP
321 assert(m);
322 assert(message);
323 assert(handler);
324
70a5db58
LP
325 r = sd_bus_message_read(message, "s", &user_name);
326 if (r < 0)
327 return r;
328
0e80e355
LP
329 r = lookup_user_name(m, message, user_name, error, &h);
330 if (r < 0)
331 return r;
70a5db58
LP
332
333 return handler(message, h, error);
334}
335
336static int method_activate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
337 return generic_home_method(userdata, message, bus_home_method_activate, error);
338}
339
340static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
341 return generic_home_method(userdata, message, bus_home_method_deactivate, error);
342}
343
a4d72746 344static int validate_and_allocate_home(Manager *m, UserRecord *hr, Hashmap *blobs, Home **ret, sd_bus_error *error) {
70a5db58 345 _cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
70a5db58
LP
346 bool signed_locally;
347 Home *other;
348 int r;
349
350 assert(m);
351 assert(hr);
352 assert(ret);
353
354 r = user_record_is_supported(hr, error);
355 if (r < 0)
356 return r;
357
358 other = hashmap_get(m->homes_by_name, hr->user_name);
359 if (other)
360 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
361
75673cd8
LP
362 r = getpwnam_malloc(hr->user_name, /* ret= */ NULL);
363 if (r >= 0)
70a5db58 364 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists in the NSS user database, refusing.", hr->user_name);
75673cd8
LP
365 if (r != -ESRCH)
366 return r;
70a5db58 367
75673cd8
LP
368 r = getgrnam_malloc(hr->user_name, /* ret= */ NULL);
369 if (r >= 0)
70a5db58 370 return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s conflicts with an NSS group by the same name, refusing.", hr->user_name);
75673cd8
LP
371 if (r != -ESRCH)
372 return r;
70a5db58 373
a4d72746
AV
374 if (blobs) {
375 const char *failed = NULL;
376 r = user_record_ensure_blob_manifest(hr, blobs, &failed);
377 if (r == -EINVAL)
378 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Provided blob files do not correspond to blob manifest.");
379 if (r < 0)
380 return sd_bus_error_set_errnof(error, r, "Failed to generate hash for blob %s: %m", strnull(failed));
381 }
382
70a5db58
LP
383 r = manager_verify_user_record(m, hr);
384 switch (r) {
385
386 case USER_RECORD_UNSIGNED:
387 /* If the record is unsigned, then let's sign it with our own key */
388 r = manager_sign_user_record(m, hr, &signed_hr, error);
389 if (r < 0)
390 return r;
391
392 hr = signed_hr;
393 _fallthrough_;
394
395 case USER_RECORD_SIGNED_EXCLUSIVE:
396 signed_locally = true;
397 break;
398
399 case USER_RECORD_SIGNED:
400 case USER_RECORD_FOREIGN:
401 signed_locally = false;
402 break;
403
404 case -ENOKEY:
405 return sd_bus_error_setf(error, BUS_ERROR_BAD_SIGNATURE, "Specified user record for %s is signed by a key we don't recognize, refusing.", hr->user_name);
406
407 default:
408 return sd_bus_error_set_errnof(error, r, "Failed to validate signature for '%s': %m", hr->user_name);
409 }
410
411 if (uid_is_valid(hr->uid)) {
75673cd8
LP
412 _cleanup_free_ struct passwd *pw = NULL;
413 _cleanup_free_ struct group *gr = NULL;
414
70a5db58
LP
415 other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
416 if (other)
417 return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by home %s, refusing.", hr->uid, other->user_name);
418
75673cd8
LP
419 r = getpwuid_malloc(hr->uid, &pw);
420 if (r >= 0)
70a5db58 421 return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by NSS user %s, refusing.", hr->uid, pw->pw_name);
75673cd8
LP
422 if (r != -ESRCH)
423 return r;
70a5db58 424
75673cd8
LP
425 r = getgrgid_malloc(hr->uid, &gr);
426 if (r >= 0)
70a5db58 427 return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use as GID by NSS group %s, refusing.", hr->uid, gr->gr_name);
75673cd8
LP
428 if (r != -ESRCH)
429 return r;
70a5db58
LP
430 } else {
431 r = manager_augment_record_with_uid(m, hr);
432 if (r < 0)
433 return sd_bus_error_set_errnof(error, r, "Failed to acquire UID for '%s': %m", hr->user_name);
434 }
435
436 r = home_new(m, hr, NULL, ret);
437 if (r < 0)
438 return r;
439
440 (*ret)->signed_locally = signed_locally;
441 return r;
442}
443
444static int method_register_home(
445 sd_bus_message *message,
446 void *userdata,
447 sd_bus_error *error) {
448
449 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
99534007 450 Manager *m = ASSERT_PTR(userdata);
fc447921 451 _cleanup_(home_freep) Home *h = NULL;
70a5db58
LP
452 int r;
453
454 assert(message);
70a5db58 455
bfc0cc1a 456 r = bus_message_read_home_record(message, USER_RECORD_LOAD_EMBEDDED|USER_RECORD_PERMISSIVE, &hr, error);
70a5db58
LP
457 if (r < 0)
458 return r;
459
460 r = bus_verify_polkit_async(
461 message,
70a5db58 462 "org.freedesktop.home1.create-home",
7b36fb9f 463 /* details= */ NULL,
70a5db58
LP
464 &m->polkit_registry,
465 error);
466 if (r < 0)
467 return r;
468 if (r == 0)
469 return 1; /* Will call us back */
470
a4d72746 471 r = validate_and_allocate_home(m, hr, NULL, &h, error);
70a5db58
LP
472 if (r < 0)
473 return r;
474
475 r = home_save_record(h);
fc447921 476 if (r < 0)
70a5db58 477 return r;
fc447921
DT
478
479 TAKE_PTR(h);
70a5db58
LP
480
481 return sd_bus_reply_method_return(message, NULL);
482}
483
484static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
485 return generic_home_method(userdata, message, bus_home_method_unregister, error);
486}
487
70a5db58 488
a4d72746 489static int method_create_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
70a5db58 490 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
a4d72746
AV
491 _cleanup_hashmap_free_ Hashmap *blobs = NULL;
492 uint64_t flags = 0;
99534007 493 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
494 Home *h;
495 int r;
496
497 assert(message);
70a5db58
LP
498
499 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, &hr, error);
500 if (r < 0)
501 return r;
502
a4d72746
AV
503 if (endswith(sd_bus_message_get_member(message), "Ex")) {
504 r = bus_message_read_blobs(message, &blobs, error);
505 if (r < 0)
506 return r;
507
508 r = sd_bus_message_read(message, "t", &flags);
509 if (r < 0)
510 return r;
d94c7eef
AV
511 if ((flags & ~SD_HOMED_CREATE_FLAGS_ALL) != 0)
512 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags provided.");
a4d72746
AV
513 }
514
70a5db58
LP
515 r = bus_verify_polkit_async(
516 message,
70a5db58 517 "org.freedesktop.home1.create-home",
7b36fb9f 518 /* details= */ NULL,
70a5db58
LP
519 &m->polkit_registry,
520 error);
521 if (r < 0)
522 return r;
523 if (r == 0)
524 return 1; /* Will call us back */
525
a4d72746 526 r = validate_and_allocate_home(m, hr, blobs, &h, error);
70a5db58
LP
527 if (r < 0)
528 return r;
529
a4d72746 530 r = home_create(h, hr, blobs, flags, error);
70a5db58
LP
531 if (r < 0)
532 goto fail;
533
534 assert(r == 0);
535 h->unregister_on_failure = true;
536 assert(!h->current_operation);
537
538 r = home_set_current_message(h, message);
539 if (r < 0)
540 return r;
541
d94c7eef
AV
542 h->current_operation->call_flags = flags;
543
70a5db58
LP
544 return 1;
545
546fail:
547 (void) home_unlink_record(h);
548 h = home_free(h);
549 return r;
550}
551
552static int method_realize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
553 return generic_home_method(userdata, message, bus_home_method_realize, error);
554}
555
556static int method_remove_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
557 return generic_home_method(userdata, message, bus_home_method_remove, error);
558}
559
560static int method_fixate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
561 return generic_home_method(userdata, message, bus_home_method_fixate, error);
562}
563
564static int method_authenticate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
565 return generic_home_method(userdata, message, bus_home_method_authenticate, error);
566}
567
568static int method_update_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
569 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
a4d72746
AV
570 _cleanup_hashmap_free_ Hashmap *blobs = NULL;
571 uint64_t flags = 0;
99534007 572 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
573 Home *h;
574 int r;
575
576 assert(message);
70a5db58 577
bfc0cc1a 578 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);
70a5db58
LP
579 if (r < 0)
580 return r;
581
a4d72746
AV
582 if (endswith(sd_bus_message_get_member(message), "Ex")) {
583 r = bus_message_read_blobs(message, &blobs, error);
584 if (r < 0)
585 return r;
586
587 r = sd_bus_message_read(message, "t", &flags);
588 if (r < 0)
589 return r;
590 }
591
70a5db58
LP
592 assert(hr->user_name);
593
594 h = hashmap_get(m->homes_by_name, hr->user_name);
595 if (!h)
596 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", hr->user_name);
597
7555fc91 598 return bus_home_update_record(h, message, hr, blobs, flags, error);
70a5db58
LP
599}
600
601static int method_resize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
602 return generic_home_method(userdata, message, bus_home_method_resize, error);
603}
604
605static int method_change_password_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
606 return generic_home_method(userdata, message, bus_home_method_change_password, error);
607}
608
609static int method_lock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
610 return generic_home_method(userdata, message, bus_home_method_lock, error);
611}
612
613static int method_unlock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
614 return generic_home_method(userdata, message, bus_home_method_unlock, error);
615}
616
617static int method_acquire_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
618 return generic_home_method(userdata, message, bus_home_method_acquire, error);
619}
620
621static int method_ref_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
622 return generic_home_method(userdata, message, bus_home_method_ref, error);
623}
624
625static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
626 return generic_home_method(userdata, message, bus_home_method_release, error);
627}
628
629static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
630 _cleanup_(operation_unrefp) Operation *o = NULL;
631 bool waiting = false;
99534007 632 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
633 Home *h;
634 int r;
635
70a5db58
LP
636 /* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
637 * for every suitable home we have and only when all of them completed we send a reply indicating
638 * completion. */
639
90e74a66 640 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58 641
64b74c86 642 if (!home_shall_suspend(h))
70a5db58
LP
643 continue;
644
645 if (!o) {
646 o = operation_new(OPERATION_LOCK_ALL, message);
647 if (!o)
648 return -ENOMEM;
649 }
650
80ace4f2 651 log_info("Automatically locking home of user %s.", h->user_name);
70a5db58
LP
652
653 r = home_schedule_operation(h, o, error);
654 if (r < 0)
655 return r;
656
657 waiting = true;
658 }
659
660 if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will
d1f6e01e
LP
661 * be sent as soon as the last of the lock operations completed. */
662 return 1;
663
664 return sd_bus_reply_method_return(message, NULL);
665}
666
667static int method_deactivate_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
668 _cleanup_(operation_unrefp) Operation *o = NULL;
669 bool waiting = false;
99534007 670 Manager *m = ASSERT_PTR(userdata);
d1f6e01e
LP
671 Home *h;
672 int r;
673
d1f6e01e
LP
674 /* This is called from systemd-homed-activate.service's ExecStop= command to ensure that all home
675 * directories are shutdown before the system goes down. Note that we don't do this from
676 * systemd-homed.service itself since we want to allow restarting of it without tearing down all home
677 * directories. */
678
679 HASHMAP_FOREACH(h, m->homes_by_name) {
680
681 if (!o) {
682 o = operation_new(OPERATION_DEACTIVATE_ALL, message);
683 if (!o)
684 return -ENOMEM;
685 }
686
687 log_info("Automatically deactivating home of user %s.", h->user_name);
688
689 r = home_schedule_operation(h, o, error);
690 if (r < 0)
691 return r;
692
693 waiting = true;
694 }
695
696 if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will be
697 * sent as soon as the last of the deactivation operations completed. */
70a5db58
LP
698 return 1;
699
700 return sd_bus_reply_method_return(message, NULL);
701}
702
49505916 703static int method_rebalance(sd_bus_message *message, void *userdata, sd_bus_error *error) {
99534007 704 Manager *m = ASSERT_PTR(userdata);
49505916
LP
705 int r;
706
49505916
LP
707 r = manager_schedule_rebalance(m, /* immediately= */ true);
708 if (r == 0)
709 return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing.");
710 if (r < 0)
711 return r;
712
713 /* Keep a reference to this message, so that we can reply to it once we are done */
714 r = set_ensure_put(&m->rebalance_queued_method_calls, &bus_message_hash_ops, message);
715 if (r < 0)
716 return log_error_errno(r, "Failed to track rebalance bus message: %m");
717
718 sd_bus_message_ref(message);
719 return 1;
720}
721
cfd508a9 722static const sd_bus_vtable manager_vtable[] = {
70a5db58
LP
723 SD_BUS_VTABLE_START(0),
724
725 SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
726
9a814166
NK
727 SD_BUS_METHOD_WITH_ARGS("GetHomeByName",
728 SD_BUS_ARGS("s", user_name),
729 SD_BUS_RESULT("u", uid,
730 "s", home_state,
731 "u", gid,
732 "s", real_name,
733 "s", home_directory,
734 "s", shell,
735 "o", bus_path),
736 method_get_home_by_name,
737 SD_BUS_VTABLE_UNPRIVILEGED),
738 SD_BUS_METHOD_WITH_ARGS("GetHomeByUID",
739 SD_BUS_ARGS("u", uid),
740 SD_BUS_RESULT("s", user_name,
741 "s", home_state,
742 "u", gid,
743 "s", real_name,
744 "s", home_directory,
745 "s", shell,
746 "o", bus_path),
747 method_get_home_by_uid,
748 SD_BUS_VTABLE_UNPRIVILEGED),
749 SD_BUS_METHOD_WITH_ARGS("GetUserRecordByName",
750 SD_BUS_ARGS("s", user_name),
751 SD_BUS_RESULT("s", user_record, "b", incomplete, "o", bus_path),
752 method_get_user_record_by_name,
753 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
754 SD_BUS_METHOD_WITH_ARGS("GetUserRecordByUID",
755 SD_BUS_ARGS("u", uid),
756 SD_BUS_RESULT("s", user_record, "b", incomplete, "o", bus_path),
757 method_get_user_record_by_uid,
758 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
759 SD_BUS_METHOD_WITH_ARGS("ListHomes",
760 SD_BUS_NO_ARGS,
761 SD_BUS_RESULT("a(susussso)", home_areas),
762 method_list_homes,
763 SD_BUS_VTABLE_UNPRIVILEGED),
70a5db58 764
162392b7 765 /* The following methods directly execute an operation on a home area, without ref-counting, queueing
18143cd7 766 * or anything, and are accessible through homectl. */
9a814166
NK
767 SD_BUS_METHOD_WITH_ARGS("ActivateHome",
768 SD_BUS_ARGS("s", user_name, "s", secret),
769 SD_BUS_NO_RESULT,
770 method_activate_home,
336b1f19
LP
771 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
772 SD_BUS_METHOD_WITH_ARGS("ActivateHomeIfReferenced",
773 SD_BUS_ARGS("s", user_name, "s", secret),
774 SD_BUS_NO_RESULT,
775 method_activate_home,
776 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
9a814166
NK
777 SD_BUS_METHOD_WITH_ARGS("DeactivateHome",
778 SD_BUS_ARGS("s", user_name),
779 SD_BUS_NO_RESULT,
780 method_deactivate_home,
781 0),
cfd508a9
ZJS
782
783 /* Add the JSON record to homed, but don't create actual $HOME */
9a814166
NK
784 SD_BUS_METHOD_WITH_ARGS("RegisterHome",
785 SD_BUS_ARGS("s", user_record),
786 SD_BUS_NO_RESULT,
787 method_register_home,
788 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
789
790 /* Remove the JSON record from homed, but don't remove actual $HOME */
9a814166
NK
791 SD_BUS_METHOD_WITH_ARGS("UnregisterHome",
792 SD_BUS_ARGS("s", user_name),
793 SD_BUS_NO_RESULT,
794 method_unregister_home,
795 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
796
797 /* Add JSON record, and create $HOME for it */
9a814166
NK
798 SD_BUS_METHOD_WITH_ARGS("CreateHome",
799 SD_BUS_ARGS("s", user_record),
800 SD_BUS_NO_RESULT,
801 method_create_home,
802 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
a4d72746
AV
803 SD_BUS_METHOD_WITH_ARGS("CreateHomeEx",
804 SD_BUS_ARGS("s", user_record, "a{sh}", blobs, "t", flags),
805 SD_BUS_NO_RESULT,
806 method_create_home,
807 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
808
809 /* Create $HOME for already registered JSON entry */
9a814166
NK
810 SD_BUS_METHOD_WITH_ARGS("RealizeHome",
811 SD_BUS_ARGS("s", user_name, "s", secret),
812 SD_BUS_NO_RESULT,
813 method_realize_home,
814 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
815
816 /* Remove the JSON record and remove $HOME */
9a814166
NK
817 SD_BUS_METHOD_WITH_ARGS("RemoveHome",
818 SD_BUS_ARGS("s", user_name),
819 SD_BUS_NO_RESULT,
820 method_remove_home,
821 SD_BUS_VTABLE_UNPRIVILEGED),
cfd508a9
ZJS
822
823 /* Investigate $HOME and propagate contained JSON record into our database */
9a814166
NK
824 SD_BUS_METHOD_WITH_ARGS("FixateHome",
825 SD_BUS_ARGS("s", user_name, "s", secret),
826 SD_BUS_NO_RESULT,
827 method_fixate_home,
828 SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
829
830 /* Just check credentials */
9a814166
NK
831 SD_BUS_METHOD_WITH_ARGS("AuthenticateHome",
832 SD_BUS_ARGS("s", user_name, "s", secret),
833 SD_BUS_NO_RESULT,
834 method_authenticate_home,
835 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
836
837 /* Update the JSON record of existing user */
9a814166
NK
838 SD_BUS_METHOD_WITH_ARGS("UpdateHome",
839 SD_BUS_ARGS("s", user_record),
840 SD_BUS_NO_RESULT,
841 method_update_home,
842 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
a4d72746
AV
843 SD_BUS_METHOD_WITH_ARGS("UpdateHomeEx",
844 SD_BUS_ARGS("s", user_record, "a{sh}", blobs, "t", flags),
845 SD_BUS_NO_RESULT,
846 method_update_home,
847 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
9a814166
NK
848
849 SD_BUS_METHOD_WITH_ARGS("ResizeHome",
850 SD_BUS_ARGS("s", user_name, "t", size, "s", secret),
851 SD_BUS_NO_RESULT,
852 method_resize_home,
853 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
854
855 SD_BUS_METHOD_WITH_ARGS("ChangePasswordHome",
856 SD_BUS_ARGS("s", user_name, "s", new_secret, "s", old_secret),
857 SD_BUS_NO_RESULT,
858 method_change_password_home,
859 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
860
861 /* Prepare active home for system suspend: flush out passwords, suspend access */
9a814166
NK
862 SD_BUS_METHOD_WITH_ARGS("LockHome",
863 SD_BUS_ARGS("s", user_name),
864 SD_BUS_NO_RESULT,
865 method_lock_home,
866 0),
cfd508a9
ZJS
867
868 /* Make $HOME usable after system resume again */
9a814166
NK
869 SD_BUS_METHOD_WITH_ARGS("UnlockHome",
870 SD_BUS_ARGS("s", user_name, "s", secret),
871 SD_BUS_NO_RESULT,
872 method_unlock_home,
873 SD_BUS_VTABLE_SENSITIVE),
cfd508a9
ZJS
874
875 /* The following methods implement ref-counted activation, and are what the PAM module and "homectl
876 * with" use. In contrast to the methods above which fail if an operation is already being executed
877 * on a home directory, these ones will queue the request, and are thus more reliable. Moreover,
878 * they are a bit smarter: AcquireHome() will fixate, activate, unlock, or authenticate depending on
879 * the state of the home area, so that the end result is always the same (i.e. the home directory is
880 * accessible), and we always validate the specified passwords. RefHome() will not authenticate, and
881 * thus only works if the home area is already active. */
9a814166
NK
882 SD_BUS_METHOD_WITH_ARGS("AcquireHome",
883 SD_BUS_ARGS("s", user_name, "s", secret, "b", please_suspend),
884 SD_BUS_RESULT("h", send_fd),
885 method_acquire_home,
886 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
887 SD_BUS_METHOD_WITH_ARGS("RefHome",
888 SD_BUS_ARGS("s", user_name, "b", please_suspend),
889 SD_BUS_RESULT("h", send_fd),
890 method_ref_home,
891 0),
508d344e
LP
892 SD_BUS_METHOD_WITH_ARGS("RefHomeUnrestricted",
893 SD_BUS_ARGS("s", user_name, "b", please_suspend),
894 SD_BUS_RESULT("h", send_fd),
895 method_ref_home,
896 0),
9a814166
NK
897 SD_BUS_METHOD_WITH_ARGS("ReleaseHome",
898 SD_BUS_ARGS("s", user_name),
899 SD_BUS_NO_RESULT,
900 method_release_home,
901 0),
70a5db58
LP
902
903 /* An operation that acts on all homes that allow it */
904 SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
d1f6e01e 905 SD_BUS_METHOD("DeactivateAllHomes", NULL, NULL, method_deactivate_all_homes, 0),
49505916 906 SD_BUS_METHOD("Rebalance", NULL, NULL, method_rebalance, 0),
70a5db58
LP
907
908 SD_BUS_VTABLE_END
909};
910
cfd508a9
ZJS
911const BusObjectImplementation manager_object = {
912 "/org/freedesktop/home1",
913 "org.freedesktop.home1.Manager",
914 .vtables = BUS_VTABLES(manager_vtable),
915 .children = BUS_IMPLEMENTATIONS(&home_object),
916};
917
70a5db58 918static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
99534007 919 Manager *m = ASSERT_PTR(userdata);
70a5db58
LP
920 int r;
921
cf536638 922 m->deferred_auto_login_event_source = sd_event_source_disable_unref(m->deferred_auto_login_event_source);
70a5db58
LP
923
924 r = sd_bus_emit_properties_changed(
925 m->bus,
926 "/org/freedesktop/home1",
927 "org.freedesktop.home1.Manager",
928 "AutoLogin", NULL);
929 if (r < 0)
930 log_warning_errno(r, "Failed to send AutoLogin property change event, ignoring: %m");
931
932 return 0;
933}
934
935int bus_manager_emit_auto_login_changed(Manager *m) {
936 int r;
937 assert(m);
938
939 if (m->deferred_auto_login_event_source)
940 return 0;
941
942 if (!m->event)
943 return 0;
944
945 if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
946 return 0;
947
948 r = sd_event_add_defer(m->event, &m->deferred_auto_login_event_source, on_deferred_auto_login, m);
949 if (r < 0)
950 return log_error_errno(r, "Failed to allocate auto login event source: %m");
951
952 r = sd_event_source_set_priority(m->deferred_auto_login_event_source, SD_EVENT_PRIORITY_IDLE+10);
953 if (r < 0)
954 log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
955
956 (void) sd_event_source_set_description(m->deferred_auto_login_event_source, "deferred-auto-login");
957 return 1;
958}