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