]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/pam_systemd_home.c
21caa5a0589372e488d3debe1f143511f6813284
[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_(sd_bus_unrefp) sd_bus *bus = NULL;
109 _cleanup_free_ char *homed_field = NULL;
110 const char *json = NULL;
111 int r;
112
113 assert(handle);
114
115 if (!username) {
116 r = pam_get_user(handle, &username, NULL);
117 if (r != PAM_SUCCESS)
118 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
119
120 if (isempty(username))
121 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
122 }
123
124 /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
125 * user names we don't consider valid. */
126 if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
127 return PAM_USER_UNKNOWN;
128
129 /* We cache the user record in the PAM context. We use a field name that includes the username, since
130 * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
131 * creates a single PAM context and first authenticates it with the user set to the originating user,
132 * then updates the user for the destination user and issues the session stack with the same PAM
133 * context. We thus must be prepared that the user record changes between calls and we keep any
134 * caching separate. */
135 homed_field = strjoin("systemd-home-user-record-", username);
136 if (!homed_field)
137 return pam_log_oom(handle);
138
139 /* Let's use the cache, so that we can share it between the session and the authentication hooks */
140 r = pam_get_data(handle, homed_field, (const void**) &json);
141 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
142 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM user record data: @PAMERR@");
143 if (r == PAM_SUCCESS && json) {
144 /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as
145 * negative cache indicator) */
146 if (json == POINTER_MAX)
147 return PAM_USER_UNKNOWN;
148 } else {
149 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
150 _cleanup_free_ char *generic_field = NULL, *json_copy = 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_(sd_bus_unrefp) sd_bus *bus = NULL;
505 _cleanup_close_ int acquired_fd = -EBADF;
506 _cleanup_free_ char *fd_field = NULL;
507 const void *home_fd_ptr = NULL;
508 const char *username = NULL;
509 unsigned n_attempts = 0;
510 int r;
511
512 assert(handle);
513
514 /* This acquires a reference to a home directory in the following ways:
515 *
516 * 1. If please_authenticate is false, it tries to call RefHome() first — which
517 * will get us a reference to the home without authentication (which will work for homes that are
518 * not encrypted, or that already are activated). If this works, we are done. Yay!
519 *
520 * 2. Otherwise, we'll call AcquireHome() — which will try to activate the home getting us a
521 * reference. If this works, we are done. Yay!
522 *
523 * 3. if ref_anyway, we'll call RefHomeUnrestricted() — which will give us a reference in any case
524 * (even if the activation failed!).
525 *
526 * The idea is that please_authenticate is set to false for the PAM session hooks (since for those
527 * authentication doesn't matter), and true for the PAM authentication hooks (since for those
528 * authentication is essential). And ref_anyway should be set if we are pretty sure that we can later
529 * activate the home directory via our fallback shell logic, and hence are OK if we can't activate
530 * things here. Usecase for that are SSH logins where SSH does the authentication and thus only the
531 * session hooks are called. But from the session hooks SSH doesn't allow asking questions, hence we
532 * simply allow the login attempt to continue but then invoke our fallback shell that will prompt the
533 * user for the missing unlock credentials, and then chainload the real shell.
534 */
535
536 r = pam_get_user(handle, &username, NULL);
537 if (r != PAM_SUCCESS)
538 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
539
540 if (isempty(username))
541 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
542
543 /* If we already have acquired the fd, let's shortcut this */
544 fd_field = strjoin("systemd-home-fd-", username);
545 if (!fd_field)
546 return pam_log_oom(handle);
547
548 r = pam_get_data(handle, fd_field, &home_fd_ptr);
549 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
550 return pam_syslog_pam_error(handle, LOG_ERR, r,
551 "Failed to retrieve PAM home reference fd: @PAMERR@");
552 if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
553 return PAM_SUCCESS;
554
555 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, bus_data);
556 if (r != PAM_SUCCESS)
557 return r;
558
559 r = acquire_user_record(handle, username, debug, &ur, bus_data);
560 if (r != PAM_SUCCESS)
561 return r;
562
563 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
564 * might happen that the record we stored on the host does not match the encryption password of the
565 * LUKS image in case the image was used in a different system where the password was changed. In
566 * that case it will happen that the LUKS password and the host password are different, and we handle
567 * that by collecting and passing multiple passwords in that case. Hence we treat bad passwords as a
568 * request to collect one more password and pass the new all all previously used passwords again. */
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 if (parse_env(handle, &flags) < 0)
754 return PAM_AUTH_ERR;
755
756 if (parse_argv(handle,
757 argc, argv,
758 &flags,
759 &debug) < 0)
760 return PAM_AUTH_ERR;
761
762 pam_debug_syslog(handle, debug, "pam-systemd-homed authenticating");
763
764 return acquire_home(handle, ACQUIRE_MUST_AUTHENTICATE|flags, debug, /* bus_data= */ NULL);
765 }
766
767 _public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int sm_flags, int argc, const char **argv) {
768 return PAM_SUCCESS;
769 }
770
771 static int fallback_shell_can_work(
772 pam_handle_t *handle,
773 AcquireHomeFlags *flags) {
774
775 const char *tty = NULL, *display = NULL;
776 int r;
777
778 assert(handle);
779 assert(flags);
780
781 r = pam_get_item_many(
782 handle,
783 PAM_TTY, &tty,
784 PAM_XDISPLAY, &display);
785 if (r != PAM_SUCCESS)
786 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
787
788 /* The fallback shell logic only works on TTY logins, hence only allow it if there's no X11 display
789 * set, and a TTY field is set that is neither "cron" (which is what crond sets, god knows why) not
790 * contains a colon (which is what various graphical X11 logins do). Note that ssh sets the tty to
791 * "ssh" here, which we allow (I mean, ssh is after all the primary reason we do all this). */
792 if (isempty(display) &&
793 tty &&
794 !strchr(tty, ':') &&
795 !streq(tty, "cron"))
796 *flags |= ACQUIRE_REF_ANYWAY; /* Allow login even if we can only ref, not activate */
797
798 return PAM_SUCCESS;
799 }
800
801 _public_ PAM_EXTERN int pam_sm_open_session(
802 pam_handle_t *handle,
803 int sm_flags,
804 int argc, const char **argv) {
805
806 /* Let's release the D-Bus connection once this function exits, after all the session might live
807 * quite a long time, and we are not going to process the bus connection in that time, so let's
808 * better close before the daemon kicks us off because we are not processing anything. */
809 _cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
810 AcquireHomeFlags flags = 0;
811 bool debug = false;
812 int r;
813
814 if (parse_env(handle, &flags) < 0)
815 return PAM_SESSION_ERR;
816
817 if (parse_argv(handle,
818 argc, argv,
819 &flags,
820 &debug) < 0)
821 return PAM_SESSION_ERR;
822
823 pam_debug_syslog(handle, debug, "pam-systemd-homed session start");
824
825 r = fallback_shell_can_work(handle, &flags);
826 if (r != PAM_SUCCESS)
827 return r;
828
829 r = acquire_home(handle, flags, debug, &d);
830 if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
831 return PAM_SUCCESS;
832 if (r != PAM_SUCCESS)
833 return r;
834
835 r = pam_putenv(handle, "SYSTEMD_HOME=1");
836 if (r != PAM_SUCCESS)
837 return pam_syslog_pam_error(handle, LOG_ERR, r,
838 "Failed to set PAM environment variable $SYSTEMD_HOME: @PAMERR@");
839
840 r = pam_putenv(handle, FLAGS_SET(flags, ACQUIRE_PLEASE_SUSPEND) ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
841 if (r != PAM_SUCCESS)
842 return pam_syslog_pam_error(handle, LOG_ERR, r,
843 "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: @PAMERR@");
844
845 return PAM_SUCCESS;
846 }
847
848 _public_ PAM_EXTERN int pam_sm_close_session(
849 pam_handle_t *handle,
850 int sm_flags,
851 int argc, const char **argv) {
852
853 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
854 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
855 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
856 const char *username = NULL;
857 bool debug = false;
858 int r;
859
860 if (parse_argv(handle,
861 argc, argv,
862 NULL,
863 &debug) < 0)
864 return PAM_SESSION_ERR;
865
866 pam_debug_syslog(handle, debug, "pam-systemd-homed session end");
867
868 r = pam_get_user(handle, &username, NULL);
869 if (r != PAM_SUCCESS)
870 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get user name: @PAMERR@");
871
872 if (isempty(username))
873 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SERVICE_ERR, "User name not set.");
874
875 /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
876 * call will be able to do its thing. */
877 r = release_home_fd(handle, username);
878 if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
879 return PAM_SUCCESS;
880 if (r != PAM_SUCCESS)
881 return r;
882
883 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, NULL);
884 if (r != PAM_SUCCESS)
885 return r;
886
887 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
888 if (r < 0)
889 return pam_bus_log_create_error(handle, r);
890
891 r = sd_bus_message_append(m, "s", username);
892 if (r < 0)
893 return pam_bus_log_create_error(handle, r);
894
895 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
896 if (r < 0) {
897 if (!sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
898 return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
899 "Failed to release user home: %s", bus_error_message(&error, r));
900
901 pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
902 }
903
904 return PAM_SUCCESS;
905 }
906
907 _public_ PAM_EXTERN int pam_sm_acct_mgmt(
908 pam_handle_t *handle,
909 int sm_flags,
910 int argc,
911 const char **argv) {
912
913 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
914 AcquireHomeFlags flags = 0;
915 bool debug = false;
916 usec_t t;
917 int r;
918
919 if (parse_env(handle, &flags) < 0)
920 return PAM_AUTH_ERR;
921
922 if (parse_argv(handle,
923 argc, argv,
924 &flags,
925 &debug) < 0)
926 return PAM_AUTH_ERR;
927
928 pam_debug_syslog(handle, debug, "pam-systemd-homed account management");
929
930 r = fallback_shell_can_work(handle, &flags);
931 if (r != PAM_SUCCESS)
932 return r;
933
934 r = acquire_home(handle, flags, debug, /* bus_data= */ NULL);
935 if (r != PAM_SUCCESS)
936 return r;
937
938 r = acquire_user_record(handle, NULL, debug, &ur, NULL);
939 if (r != PAM_SUCCESS)
940 return r;
941
942 r = user_record_test_blocked(ur);
943 switch (r) {
944
945 case -ESTALE:
946 pam_syslog(handle, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name);
947 break;
948
949 case -ENOLCK:
950 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
951 return PAM_ACCT_EXPIRED;
952
953 case -EL2HLT:
954 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
955 return PAM_ACCT_EXPIRED;
956
957 case -EL3HLT:
958 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
959 return PAM_ACCT_EXPIRED;
960
961 default:
962 if (r < 0) {
963 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
964 return PAM_ACCT_EXPIRED;
965 }
966
967 break;
968 }
969
970 t = user_record_ratelimit_next_try(ur);
971 if (t != USEC_INFINITY) {
972 usec_t n = now(CLOCK_REALTIME);
973
974 if (t > n) {
975 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
976 FORMAT_TIMESPAN(t - n, USEC_PER_SEC));
977
978 return PAM_MAXTRIES;
979 }
980 }
981
982 r = user_record_test_password_change_required(ur);
983 switch (r) {
984
985 case -EKEYREVOKED:
986 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
987 return PAM_NEW_AUTHTOK_REQD;
988
989 case -EOWNERDEAD:
990 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
991 return PAM_NEW_AUTHTOK_REQD;
992
993 /* Strictly speaking this is only about password expiration, and we might want to allow
994 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
995 case -EKEYREJECTED:
996 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
997 return PAM_AUTHTOK_EXPIRED;
998
999 case -EKEYEXPIRED:
1000 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
1001 break;
1002
1003 case -ESTALE:
1004 /* If the system clock is wrong, let's log but continue */
1005 pam_syslog(handle, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong.");
1006 break;
1007
1008 case -EROFS:
1009 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
1010 break;
1011
1012 default:
1013 if (r < 0) {
1014 (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
1015 return PAM_AUTHTOK_EXPIRED;
1016 }
1017
1018 break;
1019 }
1020
1021 return PAM_SUCCESS;
1022 }
1023
1024 _public_ PAM_EXTERN int pam_sm_chauthtok(
1025 pam_handle_t *handle,
1026 int sm_flags,
1027 int argc,
1028 const char **argv) {
1029
1030 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
1031 const char *old_password = NULL, *new_password = NULL;
1032 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
1033 unsigned n_attempts = 0;
1034 bool debug = false;
1035 int r;
1036
1037 if (parse_argv(handle,
1038 argc, argv,
1039 NULL,
1040 &debug) < 0)
1041 return PAM_AUTH_ERR;
1042
1043 pam_debug_syslog(handle, debug, "pam-systemd-homed account management");
1044
1045 r = pam_acquire_bus_connection(handle, "pam-systemd-home", &bus, NULL);
1046 if (r != PAM_SUCCESS)
1047 return r;
1048
1049 r = acquire_user_record(handle, NULL, debug, &ur, NULL);
1050 if (r != PAM_SUCCESS)
1051 return r;
1052
1053 /* Start with cached credentials */
1054 r = pam_get_item_many(
1055 handle,
1056 PAM_OLDAUTHTOK, &old_password,
1057 PAM_AUTHTOK, &new_password);
1058 if (r != PAM_SUCCESS)
1059 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get cached passwords: @PAMERR@");
1060
1061 if (isempty(new_password)) {
1062 /* No, it's not cached, then let's ask for the password and its verification, and cache
1063 * it. */
1064
1065 r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
1066 if (r != PAM_SUCCESS)
1067 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get new password: @PAMERR@");
1068
1069 if (isempty(new_password)) {
1070 pam_debug_syslog(handle, debug, "Password request aborted.");
1071 return PAM_AUTHTOK_ERR;
1072 }
1073
1074 r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
1075 if (r != PAM_SUCCESS)
1076 return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get password again: @PAMERR@");
1077
1078 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
1079 // that, and instead assume the password was already verified once when it is found to be
1080 // cached already. needs to be fixed in pam_pwquality
1081 }
1082
1083 /* Now everything is cached and checked, let's exit from the preliminary check */
1084 if (FLAGS_SET(sm_flags, PAM_PRELIM_CHECK))
1085 return PAM_SUCCESS;
1086
1087 old_secret = user_record_new();
1088 if (!old_secret)
1089 return pam_log_oom(handle);
1090
1091 if (!isempty(old_password)) {
1092 r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
1093 if (r < 0)
1094 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store old password: %m");
1095 }
1096
1097 new_secret = user_record_new();
1098 if (!new_secret)
1099 return pam_log_oom(handle);
1100
1101 r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
1102 if (r < 0)
1103 return pam_syslog_errno(handle, LOG_ERR, r, "Failed to store new password: %m");
1104
1105 for (;;) {
1106 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1107 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1108
1109 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
1110 if (r < 0)
1111 return pam_bus_log_create_error(handle, r);
1112
1113 r = sd_bus_message_append(m, "s", ur->user_name);
1114 if (r < 0)
1115 return pam_bus_log_create_error(handle, r);
1116
1117 r = bus_message_append_secret(m, new_secret);
1118 if (r < 0)
1119 return pam_bus_log_create_error(handle, r);
1120
1121 r = bus_message_append_secret(m, old_secret);
1122 if (r < 0)
1123 return pam_bus_log_create_error(handle, r);
1124
1125 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1126 if (r < 0) {
1127 r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error, debug);
1128 if (r == PAM_CONV_ERR)
1129 return pam_syslog_pam_error(handle, LOG_ERR, r,
1130 "Failed to prompt for password/prompt.");
1131 if (r != PAM_SUCCESS)
1132 return r;
1133 } else
1134 return pam_syslog_pam_error(handle, LOG_NOTICE, PAM_SUCCESS,
1135 "Successfully changed password for user %s.", ur->user_name);
1136
1137 if (++n_attempts >= 5)
1138 break;
1139
1140 /* Try again */
1141 };
1142
1143 return pam_syslog_pam_error(handle, LOG_NOTICE, PAM_MAXTRIES,
1144 "Failed to change password for user %s: @PAMERR@", ur->user_name);
1145 }