]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/pam_systemd_home.c
test: also flush and rotate journal before read
[thirdparty/systemd.git] / src / home / pam_systemd_home.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <security/pam_ext.h>
4 #include <security/pam_modules.h>
5
6 #include "sd-bus.h"
7
8 #include "bus-common-errors.h"
9 #include "bus-locator.h"
10 #include "bus-util.h"
11 #include "errno-util.h"
12 #include "fd-util.h"
13 #include "home-util.h"
14 #include "locale-util.h"
15 #include "memory-util.h"
16 #include "pam-util.h"
17 #include "parse-util.h"
18 #include "strv.h"
19 #include "user-record-util.h"
20 #include "user-record.h"
21 #include "user-util.h"
22
23 typedef enum AcquireHomeFlags {
24 ACQUIRE_MUST_AUTHENTICATE = 1 << 0,
25 ACQUIRE_PLEASE_SUSPEND = 1 << 1,
26 ACQUIRE_REF_ANYWAY = 1 << 2,
27 } AcquireHomeFlags;
28
29 static int parse_argv(
30 pam_handle_t *handle,
31 int argc, const char **argv,
32 AcquireHomeFlags *flags,
33 bool *debug) {
34
35 assert(argc >= 0);
36 assert(argc == 0 || argv);
37
38 for (int i = 0; i < argc; i++) {
39 const char *v;
40
41 if ((v = startswith(argv[i], "suspend="))) {
42 int k;
43
44 k = parse_boolean(v);
45 if (k < 0)
46 pam_syslog(handle, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v);
47 else if (flags)
48 SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, k);
49
50 } else if (streq(argv[i], "debug")) {
51 if (debug)
52 *debug = true;
53
54 } else if ((v = startswith(argv[i], "debug="))) {
55 int k;
56 k = parse_boolean(v);
57 if (k < 0)
58 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
59 else if (debug)
60 *debug = k;
61
62 } else
63 pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
64 }
65
66 return 0;
67 }
68
69 static int parse_env(
70 pam_handle_t *handle,
71 AcquireHomeFlags *flags) {
72
73 const char *v;
74 int r;
75
76 /* Let's read the suspend setting from an env var in addition to the PAM command line. That makes it
77 * easy to declare the features of a display manager in code rather than configuration, and this is
78 * really a feature of code */
79
80 v = pam_getenv(handle, "SYSTEMD_HOME_SUSPEND");
81 if (!v) {
82 /* Also check the process env block, so that people can control this via an env var from the
83 * outside of our process. */
84 v = secure_getenv("SYSTEMD_HOME_SUSPEND");
85 if (!v)
86 return 0;
87 }
88
89 r = parse_boolean(v);
90 if (r < 0)
91 pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v);
92 else if (flags)
93 SET_FLAG(*flags, ACQUIRE_PLEASE_SUSPEND, r);
94
95 return 0;
96 }
97
98 static int acquire_user_record(
99 pam_handle_t *handle,
100 const char *username,
101 bool debug,
102 UserRecord **ret_record,
103 PamBusData **bus_data) {
104
105 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
106 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
107 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
108 _cleanup_free_ char *homed_field = NULL;
109 const char *json = NULL;
110 int r;
111
112 assert(handle);
113
114 if (!username) {
115 r = pam_get_user(handle, &username, NULL);
116 if (r != PAM_SUCCESS)
117 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
118
119 if (isempty(username))
120 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
121 }
122
123 /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
124 * user names we don't consider valid. */
125 if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
126 return PAM_USER_UNKNOWN;
127
128 /* We cache the user record in the PAM context. We use a field name that includes the username, since
129 * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
130 * creates a single PAM context and first authenticates it with the user set to the originating user,
131 * then updates the user for the destination user and issues the session stack with the same PAM
132 * context. We thus must be prepared that the user record changes between calls and we keep any
133 * caching separate. */
134 homed_field = strjoin("systemd-home-user-record-", username);
135 if (!homed_field)
136 return pam_log_oom(handle);
137
138 /* Let's use the cache, so that we can share it between the session and the authentication hooks */
139 r = pam_get_data(handle, homed_field, (const void**) &json);
140 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
141 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM user record data: @PAMERR@");
142 if (r == PAM_SUCCESS && json) {
143 /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as
144 * negative cache indicator) */
145 if (json == POINTER_MAX)
146 return PAM_USER_UNKNOWN;
147 } else {
148 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
149 _cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
150 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
151
152 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, bus_data);
153 if (r != PAM_SUCCESS)
154 return r;
155
156 r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
157 if (r < 0) {
158 if (bus_error_is_unknown_service(&error)) {
159 pam_debug_syslog(handle, debug,
160 "systemd-homed is not available: %s",
161 bus_error_message(&error, r));
162 goto user_unknown;
163 }
164
165 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_HOME)) {
166 pam_debug_syslog(handle, debug,
167 "Not a user managed by systemd-homed: %s",
168 bus_error_message(&error, r));
169 goto user_unknown;
170 }
171
172 pam_syslog(handle, LOG_ERR,
173 "Failed to query user record: %s", bus_error_message(&error, r));
174 return PAM_SERVICE_ERR;
175 }
176
177 r = sd_bus_message_read(reply, "sbo", &json, NULL, NULL);
178 if (r < 0)
179 return pam_bus_log_parse_error(handle, r);
180
181 /* First copy: for the homed-specific data field, i.e. where we know the user record is from
182 * homed */
183 json_copy = strdup(json);
184 if (!json_copy)
185 return pam_log_oom(handle);
186
187 r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
188 if (r != PAM_SUCCESS)
189 return pam_syslog_pam_error(handle, LOG_ERR, r,
190 "Failed to set PAM user record data '%s': @PAMERR@", homed_field);
191
192 /* Take a second copy: for the generic data field, the one which we share with
193 * pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
194 * and non-homed user records. */
195 json_copy = strdup(json);
196 if (!json_copy)
197 return pam_log_oom(handle);
198
199 generic_field = strjoin("systemd-user-record-", username);
200 if (!generic_field)
201 return pam_log_oom(handle);
202
203 r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
204 if (r != PAM_SUCCESS)
205 return pam_syslog_pam_error(handle, LOG_ERR, r,
206 "Failed to set PAM user record data '%s': @PAMERR@", homed_field);
207
208 TAKE_PTR(json_copy);
209 }
210
211 r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
212 if (r < 0)
213 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse JSON user record: %m");
214
215 ur = user_record_new();
216 if (!ur)
217 return pam_log_oom(handle);
218
219 r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
220 if (r < 0)
221 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to load user record: %m");
222
223 /* Safety check if cached record actually matches what we are looking for */
224 if (!streq_ptr(username, ur->user_name))
225 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR,
226 "Acquired user record does not match user name.");
227
228 if (ret_record)
229 *ret_record = TAKE_PTR(ur);
230
231 return PAM_SUCCESS;
232
233 user_unknown:
234 /* Cache this, so that we don't check again */
235 r = pam_set_data(handle, homed_field, POINTER_MAX, NULL);
236 if (r != PAM_SUCCESS)
237 pam_syslog_pam_error(handle, LOG_ERR, r,
238 "Failed to set PAM user record data '%s' to invalid, ignoring: @PAMERR@",
239 homed_field);
240
241 return PAM_USER_UNKNOWN;
242 }
243
244 static int release_user_record(pam_handle_t *handle, const char *username) {
245 _cleanup_free_ char *homed_field = NULL, *generic_field = NULL;
246 int r, k;
247
248 assert(handle);
249 assert(username);
250
251 homed_field = strjoin("systemd-home-user-record-", username);
252 if (!homed_field)
253 return pam_log_oom(handle);
254
255 r = pam_set_data(handle, homed_field, NULL, NULL);
256 if (r != PAM_SUCCESS)
257 pam_syslog_pam_error(handle, LOG_ERR, r,
258 "Failed to release PAM user record data '%s': @PAMERR@", homed_field);
259
260 generic_field = strjoin("systemd-user-record-", username);
261 if (!generic_field)
262 return pam_log_oom(handle);
263
264 k = pam_set_data(handle, generic_field, NULL, NULL);
265 if (k != PAM_SUCCESS)
266 pam_syslog_pam_error(handle, LOG_ERR, k,
267 "Failed to release PAM user record data '%s': @PAMERR@", generic_field);
268
269 return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
270 }
271
272 static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
273 safe_close(PTR_TO_FD(data));
274 }
275
276 static int handle_generic_user_record_error(
277 pam_handle_t *handle,
278 const char *user_name,
279 UserRecord *secret,
280 int ret,
281 const sd_bus_error *error,
282 bool debug) {
283
284 int r;
285
286 assert(user_name);
287 assert(error);
288
289 /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
290
291 if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
292 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL,
293 _("Home of user %s is currently absent, please plug in the necessary storage device or backing file system."), user_name);
294 return pam_syslog_pam_error(handle, LOG_ERR, PAM_PERM_DENIED,
295 "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
296
297 } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
298 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too frequent login attempts for user %s, try again later."), user_name);
299 return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
300 "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
301
302 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
303 _cleanup_(erase_and_freep) char *newp = NULL;
304
305 assert(secret);
306
307 /* This didn't work? Ask for an (additional?) password */
308
309 if (strv_isempty(secret->password))
310 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Password: "));
311 else {
312 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient for authentication of user %s."), user_name);
313 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, try again: "));
314 }
315 if (r != PAM_SUCCESS)
316 return PAM_CONV_ERR; /* no logging here */
317
318 if (isempty(newp)) {
319 pam_debug_syslog(handle, debug, "Password request aborted.");
320 return PAM_AUTHTOK_ERR;
321 }
322
323 r = user_record_set_password(secret, STRV_MAKE(newp), true);
324 if (r < 0)
325 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store password: %m");
326
327 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_RECOVERY_KEY)) {
328 _cleanup_(erase_and_freep) char *newp = NULL;
329
330 assert(secret);
331
332 /* Hmm, homed asks for recovery key (because no regular password is defined maybe)? Provide it. */
333
334 if (strv_isempty(secret->password))
335 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Recovery key: "));
336 else {
337 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password/recovery key incorrect or not sufficient for authentication of user %s."), user_name);
338 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, reenter recovery key: "));
339 }
340 if (r != PAM_SUCCESS)
341 return PAM_CONV_ERR; /* no logging here */
342
343 if (isempty(newp)) {
344 pam_debug_syslog(handle, debug, "Recovery key request aborted.");
345 return PAM_AUTHTOK_ERR;
346 }
347
348 r = user_record_set_password(secret, STRV_MAKE(newp), true);
349 if (r < 0)
350 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store recovery key: %m");
351
352 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
353 _cleanup_(erase_and_freep) char *newp = NULL;
354
355 assert(secret);
356
357 if (strv_isempty(secret->password)) {
358 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token of user %s not inserted."), user_name);
359 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
360 } else {
361 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient, and configured security token of user %s not inserted."), user_name);
362 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
363 }
364 if (r != PAM_SUCCESS)
365 return PAM_CONV_ERR; /* no logging here */
366
367 if (isempty(newp)) {
368 pam_debug_syslog(handle, debug, "Password request aborted.");
369 return PAM_AUTHTOK_ERR;
370 }
371
372 r = user_record_set_password(secret, STRV_MAKE(newp), true);
373 if (r < 0)
374 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store password: %m");
375
376 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
377 _cleanup_(erase_and_freep) char *newp = NULL;
378
379 assert(secret);
380
381 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Security token PIN: "));
382 if (r != PAM_SUCCESS)
383 return PAM_CONV_ERR; /* no logging here */
384
385 if (isempty(newp)) {
386 pam_debug_syslog(handle, debug, "PIN request aborted.");
387 return PAM_AUTHTOK_ERR;
388 }
389
390 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
391 if (r < 0)
392 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store PIN: %m");
393
394 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
395
396 assert(secret);
397
398 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please authenticate physically on security token of user %s."), user_name);
399
400 r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
401 if (r < 0)
402 return pam_syslog_errno(handle, LOG_ERR, r,
403 "Failed to set PKCS#11 protected authentication path permitted flag: %m");
404
405 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
406
407 assert(secret);
408
409 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please confirm presence on security token of user %s."), user_name);
410
411 r = user_record_set_fido2_user_presence_permitted(secret, true);
412 if (r < 0)
413 return pam_syslog_errno(handle, LOG_ERR, r,
414 "Failed to set FIDO2 user presence permitted flag: %m");
415
416 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
417
418 assert(secret);
419
420 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please verify user on security token of user %s."), user_name);
421
422 r = user_record_set_fido2_user_verification_permitted(secret, true);
423 if (r < 0)
424 return pam_syslog_errno(handle, LOG_ERR, r,
425 "Failed to set FIDO2 user verification permitted flag: %m");
426
427 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
428
429 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"));
430 return PAM_SERVICE_ERR;
431
432 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
433 _cleanup_(erase_and_freep) char *newp = NULL;
434
435 assert(secret);
436
437 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN incorrect for user %s."), user_name);
438 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
439 if (r != PAM_SUCCESS)
440 return PAM_CONV_ERR; /* no logging here */
441
442 if (isempty(newp)) {
443 pam_debug_syslog(handle, debug, "PIN request aborted.");
444 return PAM_AUTHTOK_ERR;
445 }
446
447 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
448 if (r < 0)
449 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store PIN: %m");
450
451 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
452 _cleanup_(erase_and_freep) char *newp = NULL;
453
454 assert(secret);
455
456 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only a few tries left!)"), user_name);
457 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
458 if (r != PAM_SUCCESS)
459 return PAM_CONV_ERR; /* no logging here */
460
461 if (isempty(newp)) {
462 pam_debug_syslog(handle, debug, "PIN request aborted.");
463 return PAM_AUTHTOK_ERR;
464 }
465
466 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
467 if (r < 0)
468 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store PIN: %m");
469
470 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
471 _cleanup_(erase_and_freep) char *newp = NULL;
472
473 assert(secret);
474
475 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only one try left!)"), user_name);
476 r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
477 if (r != PAM_SUCCESS)
478 return PAM_CONV_ERR; /* no logging here */
479
480 if (isempty(newp)) {
481 pam_debug_syslog(handle, debug, "PIN request aborted.");
482 return PAM_AUTHTOK_ERR;
483 }
484
485 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
486 if (r < 0)
487 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store PIN: %m");
488
489 } else
490 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR,
491 "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
492
493 return PAM_SUCCESS;
494 }
495
496 static int acquire_home(
497 pam_handle_t *handle,
498 AcquireHomeFlags flags,
499 bool debug,
500 PamBusData **bus_data) {
501
502 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
503 bool do_auth = FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE), home_not_active = false, home_locked = false, unrestricted = false;
504 _cleanup_close_ int acquired_fd = -EBADF;
505 _cleanup_free_ char *fd_field = NULL;
506 const void *home_fd_ptr = NULL;
507 const char *username = NULL;
508 unsigned n_attempts = 0;
509 int r;
510
511 assert(handle);
512
513 /* This acquires a reference to a home directory in the following ways:
514 *
515 * 1. If please_authenticate is false, it tries to call RefHome() first — which
516 * will get us a reference to the home without authentication (which will work for homes that are
517 * not encrypted, or that already are activated). If this works, we are done. Yay!
518 *
519 * 2. Otherwise, we'll call AcquireHome() — which will try to activate the home getting us a
520 * reference. If this works, we are done. Yay!
521 *
522 * 3. if ref_anyway, we'll call RefHomeUnrestricted() — which will give us a reference in any case
523 * (even if the activation failed!).
524 *
525 * The idea is that please_authenticate is set to false for the PAM session hooks (since for those
526 * authentication doesn't matter), and true for the PAM authentication hooks (since for those
527 * authentication is essential). And ref_anyway should be set if we are pretty sure that we can later
528 * activate the home directory via our fallback shell logic, and hence are OK if we can't activate
529 * things here. Usecase for that are SSH logins where SSH does the authentication and thus only the
530 * session hooks are called. But from the session hooks SSH doesn't allow asking questions, hence we
531 * simply allow the login attempt to continue but then invoke our fallback shell that will prompt the
532 * user for the missing unlock credentials, and then chainload the real shell.
533 */
534
535 r = pam_get_user(handle, &username, NULL);
536 if (r != PAM_SUCCESS)
537 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
538
539 if (isempty(username))
540 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
541
542 /* If we already have acquired the fd, let's shortcut this */
543 fd_field = strjoin("systemd-home-fd-", username);
544 if (!fd_field)
545 return pam_log_oom(handle);
546
547 r = pam_get_data(handle, fd_field, &home_fd_ptr);
548 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
549 return pam_syslog_pam_error(handle, LOG_ERR, r,
550 "Failed to retrieve PAM home reference fd: @PAMERR@");
551 if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
552 return PAM_SUCCESS;
553
554 r = acquire_user_record(handle, username, debug, &ur, bus_data);
555 if (r != PAM_SUCCESS)
556 return r;
557
558 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
559 * might happen that the record we stored on the host does not match the encryption password of the
560 * LUKS image in case the image was used in a different system where the password was changed. In
561 * that case it will happen that the LUKS password and the host password are different, and we handle
562 * that by collecting and passing multiple passwords in that case. Hence we treat bad passwords as a
563 * request to collect one more password and pass the new and all previously used passwords again. */
564
565 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
566 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, bus_data);
567 if (r != PAM_SUCCESS)
568 return r;
569
570 for (;;) {
571 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
572 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
573 const char *method = NULL;
574
575 if (do_auth && !secret) {
576 const char *cached_password = NULL;
577
578 secret = user_record_new();
579 if (!secret)
580 return pam_log_oom(handle);
581
582 /* If there's already a cached password, use it. But if not let's authenticate
583 * without anything, maybe some other authentication mechanism systemd-homed
584 * implements (such as PKCS#11) allows us to authenticate without anything else. */
585 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &cached_password);
586 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
587 return pam_syslog_pam_error(handle, LOG_ERR, r,
588 "Failed to get cached password: @PAMERR@");
589
590 if (!isempty(cached_password)) {
591 r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
592 if (r < 0)
593 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store password: %m");
594 }
595 }
596
597 if (do_auth)
598 method = "AcquireHome"; /* If we shall authenticate no matter what */
599 else if (unrestricted)
600 method = "RefHomeUnrestricted"; /* If we shall get a ref no matter what */
601 else
602 method = "RefHome"; /* If we shall get a ref (if possible) */
603
604 r = bus_message_new_method_call(bus, &m, bus_home_mgr, method);
605 if (r < 0)
606 return pam_bus_log_create_error(handle, r);
607
608 r = sd_bus_message_append(m, "s", ur->user_name);
609 if (r < 0)
610 return pam_bus_log_create_error(handle, r);
611
612 if (do_auth) {
613 r = bus_message_append_secret(m, secret);
614 if (r < 0)
615 return pam_bus_log_create_error(handle, r);
616 }
617
618 r = sd_bus_message_append(m, "b", FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND));
619 if (r < 0)
620 return pam_bus_log_create_error(handle, r);
621
622 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
623 if (r < 0) {
624 if (sd_bus_error_has_names(&error, BUS_ERROR_HOME_NOT_ACTIVE, BUS_ERROR_HOME_BUSY)) {
625 /* Only on RefHome(): We can't access the home directory currently, unless
626 * it's unlocked with a password. Hence, let's try this again, this time with
627 * authentication. */
628 home_not_active = true;
629 do_auth = true;
630 } else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED)) {
631 home_locked = true; /* Similar */
632 do_auth = true;
633 } else {
634 r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error, debug);
635 if (r == PAM_CONV_ERR) {
636 /* Password/PIN prompts will fail in certain environments, for example when
637 * we are called from OpenSSH's account or session hooks, or in systemd's
638 * per-service PAM logic. In that case, print a friendly message and accept
639 * failure. */
640
641 if (!FLAGS_SET(flags, ACQUIRE_REF_ANYWAY)) {
642 if (home_not_active)
643 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently not active, please log in locally first."), ur->user_name);
644 if (home_locked)
645 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name);
646
647 if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) || debug)
648 pam_syslog(handle, FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
649
650 return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
651 }
652
653 /* ref_anyway is true, hence let's now get a ref no matter what. */
654 unrestricted = true;
655 do_auth = false;
656 } else if (r != PAM_SUCCESS)
657 return r;
658 else
659 do_auth = true; /* The issue was dealt with, some more information was collected. Let's try to authenticate, again. */
660 }
661 } else {
662 int fd;
663
664 r = sd_bus_message_read(reply, "h", &fd);
665 if (r < 0)
666 return pam_bus_log_parse_error(handle, r);
667
668 acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
669 if (acquired_fd < 0)
670 return pam_syslog_errno(handle, LOG_ERR, errno,
671 "Failed to duplicate acquired fd: %m");
672 break;
673 }
674
675 if (++n_attempts >= 5) {
676 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL,
677 _("Too many unsuccessful login attempts for user %s, refusing."), ur->user_name);
678 return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
679 "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
680 }
681 }
682
683 /* Later PAM modules may need the auth token, but only during pam_authenticate. */
684 if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) && !strv_isempty(secret->password)) {
685 r = pam_set_item(handle, PAM_AUTHTOK, *secret->password);
686 if (r != PAM_SUCCESS)
687 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
688 }
689
690 r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
691 if (r != PAM_SUCCESS)
692 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM bus data: @PAMERR@");
693 TAKE_FD(acquired_fd);
694
695 if (do_auth) {
696 /* We likely just activated the home directory, let's flush out the user record, since a
697 * newer embedded user record might have been acquired from the activation. */
698
699 r = release_user_record(handle, ur->user_name);
700 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
701 return r;
702 }
703
704 /* If we didn't actually manage to unlock the home directory, then we rely on the fallback-shell to
705 * unlock it for us. But until that happens we don't want that logind spawns the per-user service
706 * manager for us (since it would see an inaccessible home directory). Hence set an environment
707 * variable that pam_systemd looks for). */
708 if (unrestricted) {
709 r = pam_putenv(handle, "XDG_SESSION_INCOMPLETE=1");
710 if (r != PAM_SUCCESS)
711 return pam_syslog_pam_error(handle, LOG_WARNING, r, "Failed to set XDG_SESSION_INCOMPLETE= environment variable: @PAMERR@");
712
713 pam_syslog(handle, LOG_NOTICE, "Home for user %s acquired in incomplete mode, requires later activation.", ur->user_name);
714 } else
715 pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
716
717 return PAM_SUCCESS;
718 }
719
720 static int release_home_fd(pam_handle_t *handle, const char *username) {
721 _cleanup_free_ char *fd_field = NULL;
722 const void *home_fd_ptr = NULL;
723 int r;
724
725 assert(handle);
726 assert(username);
727
728 fd_field = strjoin("systemd-home-fd-", username);
729 if (!fd_field)
730 return pam_log_oom(handle);
731
732 r = pam_get_data(handle, fd_field, &home_fd_ptr);
733 if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0))
734 return PAM_NO_MODULE_DATA;
735 if (r != PAM_SUCCESS)
736 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to retrieve PAM home reference fd: @PAMERR@");
737
738 r = pam_set_data(handle, fd_field, NULL, NULL);
739 if (r != PAM_SUCCESS)
740 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to release PAM home reference fd: @PAMERR@");
741
742 return PAM_SUCCESS;
743 }
744
745 _public_ PAM_EXTERN int pam_sm_authenticate(
746 pam_handle_t *handle,
747 int sm_flags,
748 int argc, const char **argv) {
749
750 AcquireHomeFlags flags = 0;
751 bool debug = false;
752
753 pam_log_setup();
754
755 if (parse_env(handle, &flags) < 0)
756 return PAM_AUTH_ERR;
757
758 if (parse_argv(handle,
759 argc, argv,
760 &flags,
761 &debug) < 0)
762 return PAM_AUTH_ERR;
763
764 pam_debug_syslog(handle, debug, "pam-systemd-homed authenticating");
765
766 return acquire_home(handle, ACQUIRE_MUST_AUTHENTICATE|flags, debug, /* bus_data= */ NULL);
767 }
768
769 _public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int sm_flags, int argc, const char **argv) {
770 return PAM_SUCCESS;
771 }
772
773 static int fallback_shell_can_work(
774 pam_handle_t *handle,
775 AcquireHomeFlags *flags) {
776
777 const char *tty = NULL, *display = NULL;
778 int r;
779
780 assert(handle);
781 assert(flags);
782
783 r = pam_get_item_many(
784 handle,
785 PAM_TTY, &tty,
786 PAM_XDISPLAY, &display);
787 if (r != PAM_SUCCESS)
788 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
789
790 /* The fallback shell logic only works on TTY logins, hence only allow it if there's no X11 display
791 * set, and a TTY field is set that is neither "cron" (which is what crond sets, god knows why) not
792 * contains a colon (which is what various graphical X11 logins do). Note that ssh sets the tty to
793 * "ssh" here, which we allow (I mean, ssh is after all the primary reason we do all this). */
794 if (isempty(display) &&
795 tty &&
796 !strchr(tty, ':') &&
797 !streq(tty, "cron"))
798 *flags |= ACQUIRE_REF_ANYWAY; /* Allow login even if we can only ref, not activate */
799
800 return PAM_SUCCESS;
801 }
802
803 _public_ PAM_EXTERN int pam_sm_open_session(
804 pam_handle_t *handle,
805 int sm_flags,
806 int argc, const char **argv) {
807
808 /* Let's release the D-Bus connection once this function exits, after all the session might live
809 * quite a long time, and we are not going to process the bus connection in that time, so let's
810 * better close before the daemon kicks us off because we are not processing anything. */
811 _cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
812 AcquireHomeFlags flags = 0;
813 bool debug = false;
814 int r;
815
816 pam_log_setup();
817
818 if (parse_env(handle, &flags) < 0)
819 return PAM_SESSION_ERR;
820
821 if (parse_argv(handle,
822 argc, argv,
823 &flags,
824 &debug) < 0)
825 return PAM_SESSION_ERR;
826
827 pam_debug_syslog(handle, debug, "pam-systemd-homed session start");
828
829 r = fallback_shell_can_work(handle, &flags);
830 if (r != PAM_SUCCESS)
831 return r;
832
833 /* Explicitly get saved PamBusData here. Otherwise, this function may succeed without setting 'd'
834 * even if there is an opened sd-bus connection, and it will be leaked. See issue #31375. */
835 r = pam_get_bus_data(handle, "pam-systemd-home", &d);
836 if (r != PAM_SUCCESS)
837 return r;
838
839 r = acquire_home(handle, flags, debug, &d);
840 if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
841 return PAM_SUCCESS;
842 if (r != PAM_SUCCESS)
843 return r;
844
845 r = pam_putenv(handle, "SYSTEMD_HOME=1");
846 if (r != PAM_SUCCESS)
847 return pam_syslog_pam_error(handle, LOG_ERR, r,
848 "Failed to set PAM environment variable $SYSTEMD_HOME: @PAMERR@");
849
850 r = pam_putenv(handle, FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND) ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
851 if (r != PAM_SUCCESS)
852 return pam_syslog_pam_error(handle, LOG_ERR, r,
853 "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: @PAMERR@");
854
855 return PAM_SUCCESS;
856 }
857
858 _public_ PAM_EXTERN int pam_sm_close_session(
859 pam_handle_t *handle,
860 int sm_flags,
861 int argc, const char **argv) {
862
863 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
864 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
865 const char *username = NULL;
866 bool debug = false;
867 int r;
868
869 pam_log_setup();
870
871 if (parse_argv(handle,
872 argc, argv,
873 NULL,
874 &debug) < 0)
875 return PAM_SESSION_ERR;
876
877 pam_debug_syslog(handle, debug, "pam-systemd-homed session end");
878
879 r = pam_get_user(handle, &username, NULL);
880 if (r != PAM_SUCCESS)
881 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
882
883 if (isempty(username))
884 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
885
886 /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
887 * call will be able to do its thing. */
888 r = release_home_fd(handle, username);
889 if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
890 return PAM_SUCCESS;
891 if (r != PAM_SUCCESS)
892 return r;
893
894 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
895 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, NULL);
896 if (r != PAM_SUCCESS)
897 return r;
898
899 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
900 if (r < 0)
901 return pam_bus_log_create_error(handle, r);
902
903 r = sd_bus_message_append(m, "s", username);
904 if (r < 0)
905 return pam_bus_log_create_error(handle, r);
906
907 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
908 if (r < 0) {
909 if (!sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
910 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
911 "Failed to release user home: %s", bus_error_message(&error, r));
912
913 pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
914 }
915
916 return PAM_SUCCESS;
917 }
918
919 _public_ PAM_EXTERN int pam_sm_acct_mgmt(
920 pam_handle_t *handle,
921 int sm_flags,
922 int argc,
923 const char **argv) {
924
925 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
926 AcquireHomeFlags flags = 0;
927 bool debug = false;
928 usec_t t;
929 int r;
930
931 pam_log_setup();
932
933 if (parse_env(handle, &flags) < 0)
934 return PAM_AUTH_ERR;
935
936 if (parse_argv(handle,
937 argc, argv,
938 &flags,
939 &debug) < 0)
940 return PAM_AUTH_ERR;
941
942 pam_debug_syslog(handle, debug, "pam-systemd-homed account management");
943
944 r = fallback_shell_can_work(handle, &flags);
945 if (r != PAM_SUCCESS)
946 return r;
947
948 r = acquire_home(handle, flags, debug, /* bus_data= */ NULL);
949 if (r != PAM_SUCCESS)
950 return r;
951
952 r = acquire_user_record(handle, NULL, debug, &ur, NULL);
953 if (r != PAM_SUCCESS)
954 return r;
955
956 r = user_record_test_blocked(ur);
957 switch (r) {
958
959 case -ESTALE:
960 pam_syslog(handle, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name);
961 break;
962
963 case -ENOLCK:
964 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
965 return PAM_ACCT_EXPIRED;
966
967 case -EL2HLT:
968 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
969 return PAM_ACCT_EXPIRED;
970
971 case -EL3HLT:
972 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
973 return PAM_ACCT_EXPIRED;
974
975 default:
976 if (r < 0) {
977 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
978 return PAM_ACCT_EXPIRED;
979 }
980
981 break;
982 }
983
984 t = user_record_ratelimit_next_try(ur);
985 if (t != USEC_INFINITY) {
986 usec_t n = now(CLOCK_REALTIME);
987
988 if (t > n) {
989 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
990 FORMAT_TIMESPAN(t - n, USEC_PER_SEC));
991
992 return PAM_MAXTRIES;
993 }
994 }
995
996 r = user_record_test_password_change_required(ur);
997 switch (r) {
998
999 case -EKEYREVOKED:
1000 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
1001 return PAM_NEW_AUTHTOK_REQD;
1002
1003 case -EOWNERDEAD:
1004 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
1005 return PAM_NEW_AUTHTOK_REQD;
1006
1007 /* Strictly speaking this is only about password expiration, and we might want to allow
1008 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
1009 case -EKEYREJECTED:
1010 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
1011 return PAM_AUTHTOK_EXPIRED;
1012
1013 case -EKEYEXPIRED:
1014 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
1015 break;
1016
1017 case -ESTALE:
1018 /* If the system clock is wrong, let's log but continue */
1019 pam_syslog(handle, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong.");
1020 break;
1021
1022 case -EROFS:
1023 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
1024 break;
1025
1026 default:
1027 if (r < 0) {
1028 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
1029 return PAM_AUTHTOK_EXPIRED;
1030 }
1031
1032 break;
1033 }
1034
1035 return PAM_SUCCESS;
1036 }
1037
1038 _public_ PAM_EXTERN int pam_sm_chauthtok(
1039 pam_handle_t *handle,
1040 int sm_flags,
1041 int argc,
1042 const char **argv) {
1043
1044 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
1045 const char *old_password = NULL, *new_password = NULL;
1046 unsigned n_attempts = 0;
1047 bool debug = false;
1048 int r;
1049
1050 pam_log_setup();
1051
1052 if (parse_argv(handle,
1053 argc, argv,
1054 NULL,
1055 &debug) < 0)
1056 return PAM_AUTH_ERR;
1057
1058 pam_debug_syslog(handle, debug, "pam-systemd-homed account management");
1059
1060 r = acquire_user_record(handle, NULL, debug, &ur, NULL);
1061 if (r != PAM_SUCCESS)
1062 return r;
1063
1064 /* Start with cached credentials */
1065 r = pam_get_item_many(
1066 handle,
1067 PAM_OLDAUTHTOK, &old_password,
1068 PAM_AUTHTOK, &new_password);
1069 if (r != PAM_SUCCESS)
1070 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get cached passwords: @PAMERR@");
1071
1072 if (isempty(new_password)) {
1073 /* No, it's not cached, then let's ask for the password and its verification, and cache
1074 * it. */
1075
1076 r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
1077 if (r != PAM_SUCCESS)
1078 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get new password: @PAMERR@");
1079
1080 if (isempty(new_password)) {
1081 pam_debug_syslog(handle, debug, "Password request aborted.");
1082 return PAM_AUTHTOK_ERR;
1083 }
1084
1085 r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
1086 if (r != PAM_SUCCESS)
1087 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get password again: @PAMERR@");
1088
1089 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
1090 // that, and instead assume the password was already verified once when it is found to be
1091 // cached already. needs to be fixed in pam_pwquality
1092 }
1093
1094 /* Now everything is cached and checked, let's exit from the preliminary check */
1095 if (FLAGS_SET(sm_flags, PAM_PRELIM_CHECK))
1096 return PAM_SUCCESS;
1097
1098 old_secret = user_record_new();
1099 if (!old_secret)
1100 return pam_log_oom(handle);
1101
1102 if (!isempty(old_password)) {
1103 r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
1104 if (r < 0)
1105 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store old password: %m");
1106 }
1107
1108 new_secret = user_record_new();
1109 if (!new_secret)
1110 return pam_log_oom(handle);
1111
1112 r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
1113 if (r < 0)
1114 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store new password: %m");
1115
1116 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
1117 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, NULL);
1118 if (r != PAM_SUCCESS)
1119 return r;
1120
1121 for (;;) {
1122 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1123 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1124
1125 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
1126 if (r < 0)
1127 return pam_bus_log_create_error(handle, r);
1128
1129 r = sd_bus_message_append(m, "s", ur->user_name);
1130 if (r < 0)
1131 return pam_bus_log_create_error(handle, r);
1132
1133 r = bus_message_append_secret(m, new_secret);
1134 if (r < 0)
1135 return pam_bus_log_create_error(handle, r);
1136
1137 r = bus_message_append_secret(m, old_secret);
1138 if (r < 0)
1139 return pam_bus_log_create_error(handle, r);
1140
1141 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1142 if (r < 0) {
1143 r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error, debug);
1144 if (r == PAM_CONV_ERR)
1145 return pam_syslog_pam_error(handle, LOG_ERR, r,
1146 "Failed to prompt for password/prompt.");
1147 if (r != PAM_SUCCESS)
1148 return r;
1149 } else
1150 return pam_syslog_pam_error(handle, LOG_NOTICE, PAM_SUCCESS,
1151 "Successfully changed password for user %s.", ur->user_name);
1152
1153 if (++n_attempts >= 5)
1154 break;
1155
1156 /* Try again */
1157 };
1158
1159 return pam_syslog_pam_error(handle, LOG_NOTICE, PAM_MAXTRIES,
1160 "Failed to change password for user %s: @PAMERR@", ur->user_name);
1161 }