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