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_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
109 _cleanup_free_
char *homed_field
= NULL
;
110 const char *json
= NULL
;
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@");
120 if (isempty(username
))
121 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
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
;
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
);
137 return pam_log_oom(handle
);
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
;
149 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
150 _cleanup_free_
char *generic_field
= NULL
, *json_copy
= 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_(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;
514 /* This acquires a reference to a home directory in the following ways:
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!
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!
523 * 3. if ref_anyway, we'll call RefHomeUnrestricted() — which will give us a reference in any case
524 * (even if the activation failed!).
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.
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@");
540 if (isempty(username
))
541 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
543 /* If we already have acquired the fd, let's shortcut this */
544 fd_field
= strjoin("systemd-home-fd-", username
);
546 return pam_log_oom(handle
);
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)
555 r
= pam_acquire_bus_connection(handle
, "pam-systemd-home", &bus
, bus_data
);
556 if (r
!= PAM_SUCCESS
)
559 r
= acquire_user_record(handle
, username
, debug
, &ur
, bus_data
);
560 if (r
!= PAM_SUCCESS
)
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. */
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;
753 if (parse_env(handle
, &flags
) < 0)
756 if (parse_argv(handle
,
762 pam_debug_syslog(handle
, debug
, "pam-systemd-homed authenticating");
764 return acquire_home(handle
, ACQUIRE_MUST_AUTHENTICATE
|flags
, debug
, /* bus_data= */ NULL
);
767 _public_ PAM_EXTERN
int pam_sm_setcred(pam_handle_t
*pamh
, int sm_flags
, int argc
, const char **argv
) {
771 static int fallback_shell_can_work(
772 pam_handle_t
*handle
,
773 AcquireHomeFlags
*flags
) {
775 const char *tty
= NULL
, *display
= NULL
;
781 r
= pam_get_item_many(
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@");
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
) &&
796 *flags
|= ACQUIRE_REF_ANYWAY
; /* Allow login even if we can only ref, not activate */
801 _public_ PAM_EXTERN
int pam_sm_open_session(
802 pam_handle_t
*handle
,
804 int argc
, const char **argv
) {
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;
814 if (parse_env(handle
, &flags
) < 0)
815 return PAM_SESSION_ERR
;
817 if (parse_argv(handle
,
821 return PAM_SESSION_ERR
;
823 pam_debug_syslog(handle
, debug
, "pam-systemd-homed session start");
825 r
= fallback_shell_can_work(handle
, &flags
);
826 if (r
!= PAM_SUCCESS
)
829 r
= acquire_home(handle
, flags
, debug
, &d
);
830 if (r
== PAM_USER_UNKNOWN
) /* Not managed by us? Don't complain. */
832 if (r
!= PAM_SUCCESS
)
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@");
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@");
848 _public_ PAM_EXTERN
int pam_sm_close_session(
849 pam_handle_t
*handle
,
851 int argc
, const char **argv
) {
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
;
860 if (parse_argv(handle
,
864 return PAM_SESSION_ERR
;
866 pam_debug_syslog(handle
, debug
, "pam-systemd-homed session end");
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@");
872 if (isempty(username
))
873 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not set.");
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 */
880 if (r
!= PAM_SUCCESS
)
883 r
= pam_acquire_bus_connection(handle
, "pam-systemd-home", &bus
, NULL
);
884 if (r
!= PAM_SUCCESS
)
887 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ReleaseHome");
889 return pam_bus_log_create_error(handle
, r
);
891 r
= sd_bus_message_append(m
, "s", username
);
893 return pam_bus_log_create_error(handle
, r
);
895 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
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
));
901 pam_syslog(handle
, LOG_NOTICE
, "Not deactivating home directory of %s, as it is still used.", username
);
907 _public_ PAM_EXTERN
int pam_sm_acct_mgmt(
908 pam_handle_t
*handle
,
913 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
914 AcquireHomeFlags flags
= 0;
919 if (parse_env(handle
, &flags
) < 0)
922 if (parse_argv(handle
,
928 pam_debug_syslog(handle
, debug
, "pam-systemd-homed account management");
930 r
= fallback_shell_can_work(handle
, &flags
);
931 if (r
!= PAM_SUCCESS
)
934 r
= acquire_home(handle
, flags
, debug
, /* bus_data= */ NULL
);
935 if (r
!= PAM_SUCCESS
)
938 r
= acquire_user_record(handle
, NULL
, debug
, &ur
, NULL
);
939 if (r
!= PAM_SUCCESS
)
942 r
= user_record_test_blocked(ur
);
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
);
950 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is blocked, prohibiting access."));
951 return PAM_ACCT_EXPIRED
;
954 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is not valid yet, prohibiting access."));
955 return PAM_ACCT_EXPIRED
;
958 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record is not valid anymore, prohibiting access."));
959 return PAM_ACCT_EXPIRED
;
963 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record not valid, prohibiting access."));
964 return PAM_ACCT_EXPIRED
;
970 t
= user_record_ratelimit_next_try(ur
);
971 if (t
!= USEC_INFINITY
) {
972 usec_t n
= now(CLOCK_REALTIME
);
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
));
982 r
= user_record_test_password_change_required(ur
);
986 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password change required."));
987 return PAM_NEW_AUTHTOK_REQD
;
990 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password expired, change required."));
991 return PAM_NEW_AUTHTOK_REQD
;
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. */
996 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password is expired, but can't change, refusing login."));
997 return PAM_AUTHTOK_EXPIRED
;
1000 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("Password will expire soon, please change."));
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.");
1009 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
1014 (void) pam_prompt_graceful(handle
, PAM_ERROR_MSG
, NULL
, _("User record not valid, prohibiting access."));
1015 return PAM_AUTHTOK_EXPIRED
;
1024 _public_ PAM_EXTERN
int pam_sm_chauthtok(
1025 pam_handle_t
*handle
,
1028 const char **argv
) {
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;
1037 if (parse_argv(handle
,
1041 return PAM_AUTH_ERR
;
1043 pam_debug_syslog(handle
, debug
, "pam-systemd-homed account management");
1045 r
= pam_acquire_bus_connection(handle
, "pam-systemd-home", &bus
, NULL
);
1046 if (r
!= PAM_SUCCESS
)
1049 r
= acquire_user_record(handle
, NULL
, debug
, &ur
, NULL
);
1050 if (r
!= PAM_SUCCESS
)
1053 /* Start with cached credentials */
1054 r
= pam_get_item_many(
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@");
1061 if (isempty(new_password
)) {
1062 /* No, it's not cached, then let's ask for the password and its verification, and cache
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@");
1069 if (isempty(new_password
)) {
1070 pam_debug_syslog(handle
, debug
, "Password request aborted.");
1071 return PAM_AUTHTOK_ERR
;
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@");
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
1083 /* Now everything is cached and checked, let's exit from the preliminary check */
1084 if (FLAGS_SET(sm_flags
, PAM_PRELIM_CHECK
))
1087 old_secret
= user_record_new();
1089 return pam_log_oom(handle
);
1091 if (!isempty(old_password
)) {
1092 r
= user_record_set_password(old_secret
, STRV_MAKE(old_password
), true);
1094 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store old password: %m");
1097 new_secret
= user_record_new();
1099 return pam_log_oom(handle
);
1101 r
= user_record_set_password(new_secret
, STRV_MAKE(new_password
), true);
1103 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to store new password: %m");
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
;
1109 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ChangePasswordHome");
1111 return pam_bus_log_create_error(handle
, r
);
1113 r
= sd_bus_message_append(m
, "s", ur
->user_name
);
1115 return pam_bus_log_create_error(handle
, r
);
1117 r
= bus_message_append_secret(m
, new_secret
);
1119 return pam_bus_log_create_error(handle
, r
);
1121 r
= bus_message_append_secret(m
, old_secret
);
1123 return pam_bus_log_create_error(handle
, r
);
1125 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
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
)
1134 return pam_syslog_pam_error(handle
, LOG_NOTICE
, PAM_SUCCESS
,
1135 "Successfully changed password for user %s.", ur
->user_name
);
1137 if (++n_attempts
>= 5)
1143 return pam_syslog_pam_error(handle
, LOG_NOTICE
, PAM_MAXTRIES
,
1144 "Failed to change password for user %s: @PAMERR@", ur
->user_name
);