1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <security/pam_ext.h>
4 #include <security/pam_modules.h>
8 #include "bus-common-errors.h"
9 #include "bus-locator.h"
11 #include "errno-util.h"
13 #include "home-util.h"
14 #include "locale-util.h"
15 #include "memory-util.h"
17 #include "parse-util.h"
19 #include "user-record-util.h"
20 #include "user-record.h"
21 #include "user-util.h"
23 typedef enum AcquireHomeFlags
{
24 ACQUIRE_MUST_AUTHENTICATE
= 1 << 0,
25 ACQUIRE_PLEASE_SUSPEND
= 1 << 1,
26 ACQUIRE_REF_ANYWAY
= 1 << 2,
29 static int parse_argv(
31 int argc
, const char **argv
,
32 AcquireHomeFlags
*flags
,
36 assert(argc
== 0 || argv
);
38 for (int i
= 0; i
< argc
; i
++) {
41 if ((v
= startswith(argv
[i
], "suspend="))) {
46 pam_syslog(handle
, LOG_WARNING
, "Failed to parse suspend= argument, ignoring: %s", v
);
48 SET_FLAG(*flags
, ACQUIRE_PLEASE_SUSPEND
, k
);
50 } else if (streq(argv
[i
], "debug")) {
54 } else if ((v
= startswith(argv
[i
], "debug="))) {
58 pam_syslog(handle
, LOG_WARNING
, "Failed to parse debug= argument, ignoring: %s", v
);
63 pam_syslog(handle
, LOG_WARNING
, "Unknown parameter '%s', ignoring", argv
[i
]);
71 AcquireHomeFlags
*flags
) {
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 */
80 v
= pam_getenv(handle
, "SYSTEMD_HOME_SUSPEND");
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");
91 pam_syslog(handle
, LOG_WARNING
, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v
);
93 SET_FLAG(*flags
, ACQUIRE_PLEASE_SUSPEND
, r
);
98 static int acquire_user_record(
100 const char *username
,
102 UserRecord
**ret_record
,
103 PamBusData
**bus_data
) {
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
;
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@");
119 if (isempty(username
))
120 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
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
;
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
);
136 return pam_log_oom(handle
);
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
;
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
;
152 r
= pam_acquire_bus_connection(handle
, "pam-systemd-home", &bus
, bus_data
);
153 if (r
!= PAM_SUCCESS
)
156 r
= bus_call_method(bus
, bus_home_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
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
));
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
));
172 pam_syslog(handle
, LOG_ERR
,
173 "Failed to query user record: %s", bus_error_message(&error
, r
));
174 return PAM_SERVICE_ERR
;
177 r
= sd_bus_message_read(reply
, "sbo", &json
, NULL
, NULL
);
179 return pam_bus_log_parse_error(handle
, r
);
181 /* First copy: for the homed-specific data field, i.e. where we know the user record is from
183 json_copy
= strdup(json
);
185 return pam_log_oom(handle
);
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
);
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
);
197 return pam_log_oom(handle
);
199 generic_field
= strjoin("systemd-user-record-", username
);
201 return pam_log_oom(handle
);
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
);
211 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
213 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to parse JSON user record: %m");
215 ur
= user_record_new();
217 return pam_log_oom(handle
);
219 r
= user_record_load(ur
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
221 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to load user record: %m");
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.");
229 *ret_record
= TAKE_PTR(ur
);
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@",
241 return PAM_USER_UNKNOWN
;
244 static int release_user_record(pam_handle_t
*handle
, const char *username
) {
245 _cleanup_free_
char *homed_field
= NULL
, *generic_field
= NULL
;
251 homed_field
= strjoin("systemd-home-user-record-", username
);
253 return pam_log_oom(handle
);
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
);
260 generic_field
= strjoin("systemd-user-record-", username
);
262 return pam_log_oom(handle
);
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
);
269 return IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
) ? k
: r
;
272 static void cleanup_home_fd(pam_handle_t
*handle
, void *data
, int error_status
) {
273 safe_close(PTR_TO_FD(data
));
276 static int handle_generic_user_record_error(
277 pam_handle_t
*handle
,
278 const char *user_name
,
281 const sd_bus_error
*error
,
289 /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
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
));
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
));
302 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
303 _cleanup_(erase_and_freep
) char *newp
= NULL
;
307 /* This didn't work? Ask for an (additional?) password */
309 if (strv_isempty(secret
->password
))
310 r
= pam_prompt_graceful(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, _("Password: "));
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: "));
315 if (r
!= PAM_SUCCESS
)
316 return PAM_CONV_ERR
; /* no logging here */
319 pam_debug_syslog(handle
, debug
, "Password request aborted.");
320 return PAM_AUTHTOK_ERR
;
323 r
= user_record_set_password(secret
, STRV_MAKE(newp
), true);
325 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store password: %m");
327 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_RECOVERY_KEY
)) {
328 _cleanup_(erase_and_freep
) char *newp
= NULL
;
332 /* Hmm, homed asks for recovery key (because no regular password is defined maybe)? Provide it. */
334 if (strv_isempty(secret
->password
))
335 r
= pam_prompt_graceful(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, _("Recovery key: "));
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: "));
340 if (r
!= PAM_SUCCESS
)
341 return PAM_CONV_ERR
; /* no logging here */
344 pam_debug_syslog(handle
, debug
, "Recovery key request aborted.");
345 return PAM_AUTHTOK_ERR
;
348 r
= user_record_set_password(secret
, STRV_MAKE(newp
), true);
350 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store recovery key: %m");
352 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
353 _cleanup_(erase_and_freep
) char *newp
= NULL
;
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: "));
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: "));
364 if (r
!= PAM_SUCCESS
)
365 return PAM_CONV_ERR
; /* no logging here */
368 pam_debug_syslog(handle
, debug
, "Password request aborted.");
369 return PAM_AUTHTOK_ERR
;
372 r
= user_record_set_password(secret
, STRV_MAKE(newp
), true);
374 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store password: %m");
376 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
377 _cleanup_(erase_and_freep
) char *newp
= NULL
;
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 */
386 pam_debug_syslog(handle
, debug
, "PIN request aborted.");
387 return PAM_AUTHTOK_ERR
;
390 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
392 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store PIN: %m");
394 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
398 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Please authenticate physically on security token of user %s."), user_name
);
400 r
= user_record_set_pkcs11_protected_authentication_path_permitted(secret
, true);
402 return pam_syslog_errno(handle
, LOG_ERR
, r
,
403 "Failed to set PKCS#11 protected authentication path permitted flag: %m");
405 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
409 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Please confirm presence on security token of user %s."), user_name
);
411 r
= user_record_set_fido2_user_presence_permitted(secret
, true);
413 return pam_syslog_errno(handle
, LOG_ERR
, r
,
414 "Failed to set FIDO2 user presence permitted flag: %m");
416 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED
)) {
420 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Please verify user on security token of user %s."), user_name
);
422 r
= user_record_set_fido2_user_verification_permitted(secret
, true);
424 return pam_syslog_errno(handle
, LOG_ERR
, r
,
425 "Failed to set FIDO2 user verification permitted flag: %m");
427 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
)) {
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
;
432 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
433 _cleanup_(erase_and_freep
) char *newp
= NULL
;
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 */
443 pam_debug_syslog(handle
, debug
, "PIN request aborted.");
444 return PAM_AUTHTOK_ERR
;
447 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
449 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store PIN: %m");
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
;
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 */
462 pam_debug_syslog(handle
, debug
, "PIN request aborted.");
463 return PAM_AUTHTOK_ERR
;
466 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
468 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store PIN: %m");
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
;
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 */
481 pam_debug_syslog(handle
, debug
, "PIN request aborted.");
482 return PAM_AUTHTOK_ERR
;
485 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
487 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store PIN: %m");
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
));
496 static int acquire_home(
497 pam_handle_t
*handle
,
498 AcquireHomeFlags flags
,
500 PamBusData
**bus_data
) {
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;
513 /* This acquires a reference to a home directory in the following ways:
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!
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!
522 * 3. if ref_anyway, we'll call RefHomeUnrestricted() — which will give us a reference in any case
523 * (even if the activation failed!).
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.
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@");
539 if (isempty(username
))
540 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
542 /* If we already have acquired the fd, let's shortcut this */
543 fd_field
= strjoin("systemd-home-fd-", username
);
545 return pam_log_oom(handle
);
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)
554 r
= acquire_user_record(handle
, username
, debug
, &ur
, bus_data
);
555 if (r
!= PAM_SUCCESS
)
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. */
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
)
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
;
575 if (do_auth
&& !secret
) {
576 const char *cached_password
= NULL
;
578 secret
= user_record_new();
580 return pam_log_oom(handle
);
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@");
590 if (!isempty(cached_password
)) {
591 r
= user_record_set_password(secret
, STRV_MAKE(cached_password
), true);
593 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store password: %m");
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 */
602 method
= "RefHome"; /* If we shall get a ref (if possible) */
604 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, method
);
606 return pam_bus_log_create_error(handle
, r
);
608 r
= sd_bus_message_append(m
, "s", ur
->user_name
);
610 return pam_bus_log_create_error(handle
, r
);
613 r
= bus_message_append_secret(m
, secret
);
615 return pam_bus_log_create_error(handle
, r
);
618 r
= sd_bus_message_append(m
, "b", FLAGS_SET(flags
, ACQUIRE_PLEASE_SUSPEND
));
620 return pam_bus_log_create_error(handle
, r
);
622 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
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
628 home_not_active
= true;
630 } else if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_LOCKED
)) {
631 home_locked
= true; /* Similar */
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
641 if (!FLAGS_SET(flags
, ACQUIRE_REF_ANYWAY
)) {
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
);
645 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Home of user %s is currently locked, please unlock locally first."), ur
->user_name
);
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.");
650 return home_not_active
|| home_locked
? PAM_PERM_DENIED
: PAM_CONV_ERR
;
653 /* ref_anyway is true, hence let's now get a ref no matter what. */
656 } else if (r
!= PAM_SUCCESS
)
659 do_auth
= true; /* The issue was dealt with, some more information was collected. Let's try to authenticate, again. */
664 r
= sd_bus_message_read(reply
, "h", &fd
);
666 return pam_bus_log_parse_error(handle
, r
);
668 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
670 return pam_syslog_errno(handle
, LOG_ERR
, errno
,
671 "Failed to duplicate acquired fd: %m");
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
));
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@");
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
);
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. */
699 r
= release_user_record(handle
, ur
->user_name
);
700 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
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). */
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@");
713 pam_syslog(handle
, LOG_NOTICE
, "Home for user %s acquired in incomplete mode, requires later activation.", ur
->user_name
);
715 pam_syslog(handle
, LOG_NOTICE
, "Home for user %s successfully acquired.", ur
->user_name
);
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
;
728 fd_field
= strjoin("systemd-home-fd-", username
);
730 return pam_log_oom(handle
);
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@");
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@");
745 _public_ PAM_EXTERN
int pam_sm_authenticate(
746 pam_handle_t
*handle
,
748 int argc
, const char **argv
) {
750 AcquireHomeFlags flags
= 0;
755 if (parse_env(handle
, &flags
) < 0)
758 if (parse_argv(handle
,
764 pam_debug_syslog(handle
, debug
, "pam-systemd-homed authenticating");
766 return acquire_home(handle
, ACQUIRE_MUST_AUTHENTICATE
|flags
, debug
, /* bus_data= */ NULL
);
769 _public_ PAM_EXTERN
int pam_sm_setcred(pam_handle_t
*pamh
, int sm_flags
, int argc
, const char **argv
) {
773 static int fallback_shell_can_work(
774 pam_handle_t
*handle
,
775 AcquireHomeFlags
*flags
) {
777 const char *tty
= NULL
, *display
= NULL
;
783 r
= pam_get_item_many(
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@");
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
) &&
798 *flags
|= ACQUIRE_REF_ANYWAY
; /* Allow login even if we can only ref, not activate */
803 _public_ PAM_EXTERN
int pam_sm_open_session(
804 pam_handle_t
*handle
,
806 int argc
, const char **argv
) {
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;
818 if (parse_env(handle
, &flags
) < 0)
819 return PAM_SESSION_ERR
;
821 if (parse_argv(handle
,
825 return PAM_SESSION_ERR
;
827 pam_debug_syslog(handle
, debug
, "pam-systemd-homed session start");
829 r
= fallback_shell_can_work(handle
, &flags
);
830 if (r
!= PAM_SUCCESS
)
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
)
839 r
= acquire_home(handle
, flags
, debug
, &d
);
840 if (r
== PAM_USER_UNKNOWN
) /* Not managed by us? Don't complain. */
842 if (r
!= PAM_SUCCESS
)
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@");
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@");
858 _public_ PAM_EXTERN
int pam_sm_close_session(
859 pam_handle_t
*handle
,
861 int argc
, const char **argv
) {
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
;
871 if (parse_argv(handle
,
875 return PAM_SESSION_ERR
;
877 pam_debug_syslog(handle
, debug
, "pam-systemd-homed session end");
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@");
883 if (isempty(username
))
884 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
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 */
891 if (r
!= PAM_SUCCESS
)
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
)
899 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ReleaseHome");
901 return pam_bus_log_create_error(handle
, r
);
903 r
= sd_bus_message_append(m
, "s", username
);
905 return pam_bus_log_create_error(handle
, r
);
907 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
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
));
913 pam_syslog(handle
, LOG_NOTICE
, "Not deactivating home directory of %s, as it is still used.", username
);
919 _public_ PAM_EXTERN
int pam_sm_acct_mgmt(
920 pam_handle_t
*handle
,
925 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
926 AcquireHomeFlags flags
= 0;
933 if (parse_env(handle
, &flags
) < 0)
936 if (parse_argv(handle
,
942 pam_debug_syslog(handle
, debug
, "pam-systemd-homed account management");
944 r
= fallback_shell_can_work(handle
, &flags
);
945 if (r
!= PAM_SUCCESS
)
948 r
= acquire_home(handle
, flags
, debug
, /* bus_data= */ NULL
);
949 if (r
!= PAM_SUCCESS
)
952 r
= acquire_user_record(handle
, NULL
, debug
, &ur
, NULL
);
953 if (r
!= PAM_SUCCESS
)
956 r
= user_record_test_blocked(ur
);
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
);
964 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is blocked, prohibiting access."));
965 return PAM_ACCT_EXPIRED
;
968 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is not valid yet, prohibiting access."));
969 return PAM_ACCT_EXPIRED
;
972 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is not valid anymore, prohibiting access."));
973 return PAM_ACCT_EXPIRED
;
977 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record not valid, prohibiting access."));
978 return PAM_ACCT_EXPIRED
;
984 t
= user_record_ratelimit_next_try(ur
);
985 if (t
!= USEC_INFINITY
) {
986 usec_t n
= now(CLOCK_REALTIME
);
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
));
996 r
= user_record_test_password_change_required(ur
);
1000 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password change required."));
1001 return PAM_NEW_AUTHTOK_REQD
;
1004 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password expired, change required."));
1005 return PAM_NEW_AUTHTOK_REQD
;
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. */
1010 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password is expired, but can't change, refusing login."));
1011 return PAM_AUTHTOK_EXPIRED
;
1014 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password will expire soon, please change."));
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.");
1023 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
1028 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record not valid, prohibiting access."));
1029 return PAM_AUTHTOK_EXPIRED
;
1038 _public_ PAM_EXTERN
int pam_sm_chauthtok(
1039 pam_handle_t
*handle
,
1042 const char **argv
) {
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;
1052 if (parse_argv(handle
,
1056 return PAM_AUTH_ERR
;
1058 pam_debug_syslog(handle
, debug
, "pam-systemd-homed account management");
1060 r
= acquire_user_record(handle
, NULL
, debug
, &ur
, NULL
);
1061 if (r
!= PAM_SUCCESS
)
1064 /* Start with cached credentials */
1065 r
= pam_get_item_many(
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@");
1072 if (isempty(new_password
)) {
1073 /* No, it's not cached, then let's ask for the password and its verification, and cache
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@");
1080 if (isempty(new_password
)) {
1081 pam_debug_syslog(handle
, debug
, "Password request aborted.");
1082 return PAM_AUTHTOK_ERR
;
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@");
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
1094 /* Now everything is cached and checked, let's exit from the preliminary check */
1095 if (FLAGS_SET(sm_flags
, PAM_PRELIM_CHECK
))
1098 old_secret
= user_record_new();
1100 return pam_log_oom(handle
);
1102 if (!isempty(old_password
)) {
1103 r
= user_record_set_password(old_secret
, STRV_MAKE(old_password
), true);
1105 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store old password: %m");
1108 new_secret
= user_record_new();
1110 return pam_log_oom(handle
);
1112 r
= user_record_set_password(new_secret
, STRV_MAKE(new_password
), true);
1114 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store new password: %m");
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
)
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
;
1125 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ChangePasswordHome");
1127 return pam_bus_log_create_error(handle
, r
);
1129 r
= sd_bus_message_append(m
, "s", ur
->user_name
);
1131 return pam_bus_log_create_error(handle
, r
);
1133 r
= bus_message_append_secret(m
, new_secret
);
1135 return pam_bus_log_create_error(handle
, r
);
1137 r
= bus_message_append_secret(m
, old_secret
);
1139 return pam_bus_log_create_error(handle
, r
);
1141 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
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
)
1150 return pam_syslog_pam_error(handle
, LOG_NOTICE
, PAM_SUCCESS
,
1151 "Successfully changed password for user %s.", ur
->user_name
);
1153 if (++n_attempts
>= 5)
1159 return pam_syslog_pam_error(handle
, LOG_NOTICE
, PAM_MAXTRIES
,
1160 "Failed to change password for user %s: @PAMERR@", ur
->user_name
);