1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <security/pam_ext.h>
4 #include <security/pam_modules.h>
8 #include "bus-common-errors.h"
9 #include "bus-locator.h"
10 #include "errno-util.h"
12 #include "home-util.h"
13 #include "memory-util.h"
15 #include "parse-util.h"
17 #include "user-record-util.h"
18 #include "user-record.h"
19 #include "user-util.h"
21 static int parse_argv(
23 int argc
, const char **argv
,
30 assert(argc
== 0 || argv
);
32 for (i
= 0; i
< argc
; i
++) {
35 if ((v
= startswith(argv
[i
], "suspend="))) {
40 pam_syslog(handle
, LOG_WARNING
, "Failed to parse suspend= argument, ignoring: %s", v
);
41 else if (please_suspend
)
44 } else if (streq(argv
[i
], "debug")) {
48 } else if ((v
= startswith(argv
[i
], "debug="))) {
52 pam_syslog(handle
, LOG_WARNING
, "Failed to parse debug= argument, ignoring: %s", v
);
57 pam_syslog(handle
, LOG_WARNING
, "Unknown parameter '%s', ignoring", argv
[i
]);
65 bool *please_suspend
) {
70 /* Let's read the suspend setting from an env var in addition to the PAM command line. That makes it
71 * easy to declare the features of a display manager in code rather than configuration, and this is
72 * really a feature of code */
74 v
= pam_getenv(handle
, "SYSTEMD_HOME_SUSPEND");
76 /* Also check the process env block, so that people can control this via an env var from the
77 * outside of our process. */
78 v
= secure_getenv("SYSTEMD_HOME_SUSPEND");
85 pam_syslog(handle
, LOG_WARNING
, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v
);
86 else if (please_suspend
)
92 static int acquire_user_record(
95 UserRecord
**ret_record
) {
97 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
98 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
99 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
100 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
101 _cleanup_free_
char *homed_field
= NULL
;
102 const char *json
= NULL
;
108 r
= pam_get_user(handle
, &username
, NULL
);
109 if (r
!= PAM_SUCCESS
) {
110 pam_syslog(handle
, LOG_ERR
, "Failed to get user name: %s", pam_strerror(handle
, r
));
114 if (isempty(username
)) {
115 pam_syslog(handle
, LOG_ERR
, "User name not set.");
116 return PAM_SERVICE_ERR
;
120 /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
121 * user names we don't consider valid. */
122 if (STR_IN_SET(username
, "root", NOBODY_USER_NAME
) || !valid_user_group_name(username
, 0))
123 return PAM_USER_UNKNOWN
;
125 /* We cache the user record in the PAM context. We use a field name that includes the username, since
126 * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
127 * creates a single PAM context and first authenticates it with the user set to the originating user,
128 * then updates the user for the destination user and issues the session stack with the same PAM
129 * context. We thus must be prepared that the user record changes between calls and we keep any
130 * caching separate. */
131 homed_field
= strjoin("systemd-home-user-record-", username
);
133 return pam_log_oom(handle
);
135 /* Let's use the cache, so that we can share it between the session and the authentication hooks */
136 r
= pam_get_data(handle
, homed_field
, (const void**) &json
);
137 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
)) {
138 pam_syslog(handle
, LOG_ERR
, "Failed to get PAM user record data: %s", pam_strerror(handle
, r
));
141 if (r
== PAM_SUCCESS
&& json
) {
142 /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as
143 * negative cache indicator) */
144 if (json
== (void*) -1)
145 return PAM_USER_UNKNOWN
;
147 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
148 _cleanup_free_
char *generic_field
= NULL
, *json_copy
= NULL
;
150 r
= pam_acquire_bus_connection(handle
, &bus
);
151 if (r
!= PAM_SUCCESS
)
154 r
= bus_call_method(bus
, bus_home_mgr
, "GetUserRecordByName", &error
, &reply
, "s", username
);
156 if (sd_bus_error_has_name(&error
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
157 sd_bus_error_has_name(&error
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
)) {
158 pam_syslog(handle
, LOG_DEBUG
, "systemd-homed is not available: %s", bus_error_message(&error
, r
));
162 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_HOME
)) {
163 pam_syslog(handle
, LOG_DEBUG
, "Not a user managed by systemd-homed: %s", bus_error_message(&error
, r
));
167 pam_syslog(handle
, LOG_ERR
, "Failed to query user record: %s", bus_error_message(&error
, r
));
168 return PAM_SERVICE_ERR
;
171 r
= sd_bus_message_read(reply
, "sbo", &json
, NULL
, NULL
);
173 return pam_bus_log_parse_error(handle
, r
);
175 /* First copy: for the homed-specific data field, i.e. where we know the user record is from
177 json_copy
= strdup(json
);
179 return pam_log_oom(handle
);
181 r
= pam_set_data(handle
, homed_field
, json_copy
, pam_cleanup_free
);
182 if (r
!= PAM_SUCCESS
) {
183 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM user record data '%s': %s",
184 homed_field
, pam_strerror(handle
, r
));
188 /* Take a second copy: for the generic data field, the one which we share with
189 * pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
190 * and non-homed user records. */
191 json_copy
= strdup(json
);
193 return pam_log_oom(handle
);
195 generic_field
= strjoin("systemd-user-record-", username
);
197 return pam_log_oom(handle
);
199 r
= pam_set_data(handle
, generic_field
, json_copy
, pam_cleanup_free
);
200 if (r
!= PAM_SUCCESS
) {
201 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM user record data '%s': %s",
202 homed_field
, pam_strerror(handle
, r
));
209 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
211 pam_syslog(handle
, LOG_ERR
, "Failed to parse JSON user record: %s", strerror_safe(r
));
212 return PAM_SERVICE_ERR
;
215 ur
= user_record_new();
217 return pam_log_oom(handle
);
219 r
= user_record_load(ur
, v
, USER_RECORD_LOAD_REFUSE_SECRET
);
221 pam_syslog(handle
, LOG_ERR
, "Failed to load user record: %s", strerror_safe(r
));
222 return PAM_SERVICE_ERR
;
225 /* Safety check if cached record actually matches what we are looking for */
226 if (!streq_ptr(username
, ur
->user_name
)) {
227 pam_syslog(handle
, LOG_ERR
, "Acquired user record does not match user name.");
228 return PAM_SERVICE_ERR
;
232 *ret_record
= TAKE_PTR(ur
);
237 /* Cache this, so that we don't check again */
238 r
= pam_set_data(handle
, homed_field
, (void*) -1, NULL
);
239 if (r
!= PAM_SUCCESS
)
240 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM user record data '%s' to invalid, ignoring: %s",
241 homed_field
, pam_strerror(handle
, r
));
243 return PAM_USER_UNKNOWN
;
246 static int release_user_record(pam_handle_t
*handle
, const char *username
) {
247 _cleanup_free_
char *homed_field
= NULL
, *generic_field
= NULL
;
253 homed_field
= strjoin("systemd-home-user-record-", username
);
255 return pam_log_oom(handle
);
257 r
= pam_set_data(handle
, homed_field
, NULL
, NULL
);
258 if (r
!= PAM_SUCCESS
)
259 pam_syslog(handle
, LOG_ERR
, "Failed to release PAM user record data '%s': %s", homed_field
, pam_strerror(handle
, r
));
261 generic_field
= strjoin("systemd-user-record-", username
);
263 return pam_log_oom(handle
);
265 k
= pam_set_data(handle
, generic_field
, NULL
, NULL
);
266 if (k
!= PAM_SUCCESS
)
267 pam_syslog(handle
, LOG_ERR
, "Failed to release PAM user record data '%s': %s", generic_field
, pam_strerror(handle
, k
));
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(handle
, PAM_ERROR_MSG
, NULL
, "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name
);
293 pam_syslog(handle
, LOG_ERR
, "Failed to acquire home for user %s: %s", user_name
, bus_error_message(error
, ret
));
294 return PAM_PERM_DENIED
;
296 } else if (sd_bus_error_has_name(error
, BUS_ERROR_AUTHENTICATION_LIMIT_HIT
)) {
297 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Too frequent unsuccessful login attempts for user %s, try again later.", user_name
);
298 pam_syslog(handle
, LOG_ERR
, "Failed to acquire home for user %s: %s", user_name
, bus_error_message(error
, ret
));
301 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD
)) {
302 _cleanup_(erase_and_freep
) char *newp
= NULL
;
304 /* This didn't work? Ask for an (additional?) password */
306 if (strv_isempty(secret
->password
))
307 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Password: ");
309 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password incorrect or not sufficient for authentication of user %s.", user_name
);
310 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Sorry, try again: ");
312 if (r
!= PAM_SUCCESS
)
313 return PAM_CONV_ERR
; /* no logging here */
316 pam_syslog(handle
, LOG_DEBUG
, "Password request aborted.");
317 return PAM_AUTHTOK_ERR
;
320 r
= user_record_set_password(secret
, STRV_MAKE(newp
), true);
322 pam_syslog(handle
, LOG_ERR
, "Failed to store password: %s", strerror_safe(r
));
323 return PAM_SERVICE_ERR
;
326 } else if (sd_bus_error_has_name(error
, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN
)) {
327 _cleanup_(erase_and_freep
) char *newp
= NULL
;
329 if (strv_isempty(secret
->password
)) {
330 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Security token of user %s not inserted.", user_name
);
331 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Try again with password: ");
333 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password incorrect or not sufficient, and configured security token of user %s not inserted.", user_name
);
334 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Try again with password: ");
336 if (r
!= PAM_SUCCESS
)
337 return PAM_CONV_ERR
; /* no logging here */
340 pam_syslog(handle
, LOG_DEBUG
, "Password request aborted.");
341 return PAM_AUTHTOK_ERR
;
344 r
= user_record_set_password(secret
, STRV_MAKE(newp
), true);
346 pam_syslog(handle
, LOG_ERR
, "Failed to store password: %s", strerror_safe(r
));
347 return PAM_SERVICE_ERR
;
350 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_NEEDED
)) {
351 _cleanup_(erase_and_freep
) char *newp
= NULL
;
353 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Security token PIN: ");
354 if (r
!= PAM_SUCCESS
)
355 return PAM_CONV_ERR
; /* no logging here */
358 pam_syslog(handle
, LOG_DEBUG
, "PIN request aborted.");
359 return PAM_AUTHTOK_ERR
;
362 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
364 pam_syslog(handle
, LOG_ERR
, "Failed to store PIN: %s", strerror_safe(r
));
365 return PAM_SERVICE_ERR
;
368 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED
)) {
370 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Please authenticate physically on security token of user %s.", user_name
);
372 r
= user_record_set_pkcs11_protected_authentication_path_permitted(secret
, true);
374 pam_syslog(handle
, LOG_ERR
, "Failed to set PKCS#11 protected authentication path permitted flag: %s", strerror_safe(r
));
375 return PAM_SERVICE_ERR
;
378 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED
)) {
380 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Please verify presence on security token of user %s.", user_name
);
382 r
= user_record_set_fido2_user_presence_permitted(secret
, true);
384 pam_syslog(handle
, LOG_ERR
, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r
));
385 return PAM_SERVICE_ERR
;
388 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_PIN_LOCKED
)) {
390 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
391 return PAM_SERVICE_ERR
;
393 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN
)) {
394 _cleanup_(erase_and_freep
) char *newp
= NULL
;
396 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Security token PIN incorrect for user %s.", user_name
);
397 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Sorry, retry security token PIN: ");
398 if (r
!= PAM_SUCCESS
)
399 return PAM_CONV_ERR
; /* no logging here */
402 pam_syslog(handle
, LOG_DEBUG
, "PIN request aborted.");
403 return PAM_AUTHTOK_ERR
;
406 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
408 pam_syslog(handle
, LOG_ERR
, "Failed to store PIN: %s", strerror_safe(r
));
409 return PAM_SERVICE_ERR
;
412 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT
)) {
413 _cleanup_(erase_and_freep
) char *newp
= NULL
;
415 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Security token PIN of user %s incorrect (only a few tries left!)", user_name
);
416 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Sorry, retry security token PIN: ");
417 if (r
!= PAM_SUCCESS
)
418 return PAM_CONV_ERR
; /* no logging here */
421 pam_syslog(handle
, LOG_DEBUG
, "PIN request aborted.");
422 return PAM_AUTHTOK_ERR
;
425 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
427 pam_syslog(handle
, LOG_ERR
, "Failed to store PIN: %s", strerror_safe(r
));
428 return PAM_SERVICE_ERR
;
431 } else if (sd_bus_error_has_name(error
, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT
)) {
432 _cleanup_(erase_and_freep
) char *newp
= NULL
;
434 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Security token PIN of user %s incorrect (only one try left!)", user_name
);
435 r
= pam_prompt(handle
, PAM_PROMPT_ECHO_OFF
, &newp
, "Sorry, retry security token PIN: ");
436 if (r
!= PAM_SUCCESS
)
437 return PAM_CONV_ERR
; /* no logging here */
440 pam_syslog(handle
, LOG_DEBUG
, "PIN request aborted.");
441 return PAM_AUTHTOK_ERR
;
444 r
= user_record_set_token_pin(secret
, STRV_MAKE(newp
), false);
446 pam_syslog(handle
, LOG_ERR
, "Failed to store PIN: %s", strerror_safe(r
));
447 return PAM_SERVICE_ERR
;
451 pam_syslog(handle
, LOG_ERR
, "Failed to acquire home for user %s: %s", user_name
, bus_error_message(error
, ret
));
452 return PAM_SERVICE_ERR
;
458 static int acquire_home(
459 pam_handle_t
*handle
,
460 bool please_authenticate
,
464 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
, *secret
= NULL
;
465 bool do_auth
= please_authenticate
, home_not_active
= false, home_locked
= false;
466 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
467 _cleanup_close_
int acquired_fd
= -1;
468 _cleanup_free_
char *fd_field
= NULL
;
469 const void *home_fd_ptr
= NULL
;
470 const char *username
= NULL
;
471 unsigned n_attempts
= 0;
476 /* This acquires a reference to a home directory in one of two ways: if please_authenticate is true,
477 * then we'll call AcquireHome() after asking the user for a password. Otherwise it tries to call
478 * RefHome() and if that fails queries the user for a password and uses AcquireHome().
480 * The idea is that the PAM authentication hook sets please_authenticate and thus always
481 * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
482 * authentication if possible, but with authentication if necessary. */
484 r
= pam_get_user(handle
, &username
, NULL
);
485 if (r
!= PAM_SUCCESS
) {
486 pam_syslog(handle
, LOG_ERR
, "Failed to get user name: %s", pam_strerror(handle
, r
));
490 if (isempty(username
)) {
491 pam_syslog(handle
, LOG_ERR
, "User name not set.");
492 return PAM_SERVICE_ERR
;
495 /* If we already have acquired the fd, let's shortcut this */
496 fd_field
= strjoin("systemd-home-fd-", username
);
498 return pam_log_oom(handle
);
500 r
= pam_get_data(handle
, fd_field
, &home_fd_ptr
);
501 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
)) {
502 pam_syslog(handle
, LOG_ERR
, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle
, r
));
505 if (r
== PAM_SUCCESS
&& PTR_TO_FD(home_fd_ptr
) >= 0)
508 r
= pam_acquire_bus_connection(handle
, &bus
);
509 if (r
!= PAM_SUCCESS
)
512 r
= acquire_user_record(handle
, username
, &ur
);
513 if (r
!= PAM_SUCCESS
)
516 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
517 * might happen that the record we stored on the host does not match the encryption password of
518 * the LUKS image in case the image was used in a different system where the password was
519 * changed. In that case it will happen that the LUKS password and the host password are
520 * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
521 * treat bad passwords as a request to collect one more password and pass the new all all previously
522 * used passwords again. */
525 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
526 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
528 if (do_auth
&& !secret
) {
529 const char *cached_password
= NULL
;
531 secret
= user_record_new();
533 return pam_log_oom(handle
);
535 /* If there's already a cached password, use it. But if not let's authenticate
536 * without anything, maybe some other authentication mechanism systemd-homed
537 * implements (such as PKCS#11) allows us to authenticate without anything else. */
538 r
= pam_get_item(handle
, PAM_AUTHTOK
, (const void**) &cached_password
);
539 if (!IN_SET(r
, PAM_BAD_ITEM
, PAM_SUCCESS
)) {
540 pam_syslog(handle
, LOG_ERR
, "Failed to get cached password: %s", pam_strerror(handle
, r
));
544 if (!isempty(cached_password
)) {
545 r
= user_record_set_password(secret
, STRV_MAKE(cached_password
), true);
547 pam_syslog(handle
, LOG_ERR
, "Failed to store password: %s", strerror_safe(r
));
548 return PAM_SERVICE_ERR
;
553 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, do_auth
? "AcquireHome" : "RefHome");
555 return pam_bus_log_create_error(handle
, r
);
557 r
= sd_bus_message_append(m
, "s", ur
->user_name
);
559 return pam_bus_log_create_error(handle
, r
);
562 r
= bus_message_append_secret(m
, secret
);
564 return pam_bus_log_create_error(handle
, r
);
567 r
= sd_bus_message_append(m
, "b", please_suspend
);
569 return pam_bus_log_create_error(handle
, r
);
571 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
574 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_NOT_ACTIVE
))
575 /* Only on RefHome(): We can't access the home directory currently, unless
576 * it's unlocked with a password. Hence, let's try this again, this time with
578 home_not_active
= true;
579 else if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_LOCKED
))
580 home_locked
= true; /* Similar */
582 r
= handle_generic_user_record_error(handle
, ur
->user_name
, secret
, r
, &error
);
583 if (r
== PAM_CONV_ERR
) {
584 /* Password/PIN prompts will fail in certain environments, for example when
585 * we are called from OpenSSH's account or session hooks, or in systemd's
586 * per-service PAM logic. In that case, print a friendly message and accept
590 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Home of user %s is currently not active, please log in locally first.", ur
->user_name
);
592 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Home of user %s is currently locked, please unlock locally first.", ur
->user_name
);
594 pam_syslog(handle
, please_authenticate
? LOG_ERR
: LOG_DEBUG
, "Failed to prompt for password/prompt.");
596 return home_not_active
|| home_locked
? PAM_PERM_DENIED
: PAM_CONV_ERR
;
598 if (r
!= PAM_SUCCESS
)
605 r
= sd_bus_message_read(reply
, "h", &fd
);
607 return pam_bus_log_parse_error(handle
, r
);
609 acquired_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
610 if (acquired_fd
< 0) {
611 pam_syslog(handle
, LOG_ERR
, "Failed to duplicate acquired fd: %s", bus_error_message(&error
, r
));
612 return PAM_SERVICE_ERR
;
618 if (++n_attempts
>= 5) {
619 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Too many unsuccessful login attempts for user %s, refusing.", ur
->user_name
);
620 pam_syslog(handle
, LOG_ERR
, "Failed to acquire home for user %s: %s", ur
->user_name
, bus_error_message(&error
, r
));
624 /* Try again, this time with authentication if we didn't do that before. */
628 r
= pam_set_data(handle
, fd_field
, FD_TO_PTR(acquired_fd
), cleanup_home_fd
);
630 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM bus data: %s", pam_strerror(handle
, r
));
633 TAKE_FD(acquired_fd
);
636 /* We likely just activated the home directory, let's flush out the user record, since a
637 * newer embedded user record might have been acquired from the activation. */
639 r
= release_user_record(handle
, ur
->user_name
);
640 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
644 pam_syslog(handle
, LOG_NOTICE
, "Home for user %s successfully acquired.", ur
->user_name
);
649 static int release_home_fd(pam_handle_t
*handle
, const char *username
) {
650 _cleanup_free_
char *fd_field
= NULL
;
651 const void *home_fd_ptr
= NULL
;
657 fd_field
= strjoin("systemd-home-fd-", username
);
659 return pam_log_oom(handle
);
661 r
= pam_get_data(handle
, fd_field
, &home_fd_ptr
);
662 if (r
== PAM_NO_MODULE_DATA
|| (r
== PAM_SUCCESS
&& PTR_TO_FD(home_fd_ptr
) < 0))
663 return PAM_NO_MODULE_DATA
;
664 if (r
!= PAM_SUCCESS
) {
665 pam_syslog(handle
, LOG_ERR
, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle
, r
));
669 r
= pam_set_data(handle
, fd_field
, NULL
, NULL
);
670 if (r
!= PAM_SUCCESS
)
671 pam_syslog(handle
, LOG_ERR
, "Failed to release PAM home reference fd: %s", pam_strerror(handle
, r
));
676 _public_ PAM_EXTERN
int pam_sm_authenticate(
677 pam_handle_t
*handle
,
679 int argc
, const char **argv
) {
681 bool debug
= false, suspend_please
= false;
683 if (parse_env(handle
, &suspend_please
) < 0)
686 if (parse_argv(handle
,
693 pam_syslog(handle
, LOG_DEBUG
, "pam-systemd-homed authenticating");
695 return acquire_home(handle
, /* please_authenticate= */ true, suspend_please
, debug
);
698 _public_ PAM_EXTERN
int pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
) {
702 _public_ PAM_EXTERN
int pam_sm_open_session(
703 pam_handle_t
*handle
,
705 int argc
, const char **argv
) {
707 bool debug
= false, suspend_please
= false;
710 if (parse_env(handle
, &suspend_please
) < 0)
711 return PAM_SESSION_ERR
;
713 if (parse_argv(handle
,
717 return PAM_SESSION_ERR
;
720 pam_syslog(handle
, LOG_DEBUG
, "pam-systemd-homed session start");
722 r
= acquire_home(handle
, /* please_authenticate = */ false, suspend_please
, debug
);
723 if (r
== PAM_USER_UNKNOWN
) /* Not managed by us? Don't complain. */
725 if (r
!= PAM_SUCCESS
)
728 r
= pam_putenv(handle
, "SYSTEMD_HOME=1");
729 if (r
!= PAM_SUCCESS
) {
730 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM environment variable $SYSTEMD_HOME: %s", pam_strerror(handle
, r
));
734 r
= pam_putenv(handle
, suspend_please
? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
735 if (r
!= PAM_SUCCESS
) {
736 pam_syslog(handle
, LOG_ERR
, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: %s", pam_strerror(handle
, r
));
740 /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
741 * not going to process the bus connection in that time, so let's better close before the daemon
742 * kicks us off because we are not processing anything. */
743 (void) pam_release_bus_connection(handle
);
747 _public_ PAM_EXTERN
int pam_sm_close_session(
748 pam_handle_t
*handle
,
750 int argc
, const char **argv
) {
752 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
753 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
754 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
755 const char *username
= NULL
;
759 if (parse_argv(handle
,
763 return PAM_SESSION_ERR
;
766 pam_syslog(handle
, LOG_DEBUG
, "pam-systemd-homed session end");
768 r
= pam_get_user(handle
, &username
, NULL
);
769 if (r
!= PAM_SUCCESS
) {
770 pam_syslog(handle
, LOG_ERR
, "Failed to get user name: %s", pam_strerror(handle
, r
));
774 if (isempty(username
)) {
775 pam_syslog(handle
, LOG_ERR
, "User name not set.");
776 return PAM_SERVICE_ERR
;
779 /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
780 * call will be able to do its thing. */
781 r
= release_home_fd(handle
, username
);
782 if (r
== PAM_NO_MODULE_DATA
) /* Nothing to do, we never acquired an fd */
784 if (r
!= PAM_SUCCESS
)
787 r
= pam_acquire_bus_connection(handle
, &bus
);
788 if (r
!= PAM_SUCCESS
)
791 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ReleaseHome");
793 return pam_bus_log_create_error(handle
, r
);
795 r
= sd_bus_message_append(m
, "s", username
);
797 return pam_bus_log_create_error(handle
, r
);
799 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
801 if (sd_bus_error_has_name(&error
, BUS_ERROR_HOME_BUSY
))
802 pam_syslog(handle
, LOG_NOTICE
, "Not deactivating home directory of %s, as it is still used.", username
);
804 pam_syslog(handle
, LOG_ERR
, "Failed to release user home: %s", bus_error_message(&error
, r
));
805 return PAM_SESSION_ERR
;
812 _public_ PAM_EXTERN
int pam_sm_acct_mgmt(
813 pam_handle_t
*handle
,
818 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
819 bool debug
= false, please_suspend
= false;
823 if (parse_env(handle
, &please_suspend
) < 0)
826 if (parse_argv(handle
,
833 pam_syslog(handle
, LOG_DEBUG
, "pam-systemd-homed account management");
835 r
= acquire_home(handle
, /* please_authenticate = */ false, please_suspend
, debug
);
836 if (r
== PAM_USER_UNKNOWN
)
837 return PAM_SUCCESS
; /* we don't have anything to say about users we don't manage */
838 if (r
!= PAM_SUCCESS
)
841 r
= acquire_user_record(handle
, NULL
, &ur
);
842 if (r
!= PAM_SUCCESS
)
845 r
= user_record_test_blocked(ur
);
849 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record is newer than current system time, prohibiting access.");
850 return PAM_ACCT_EXPIRED
;
853 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record is blocked, prohibiting access.");
854 return PAM_ACCT_EXPIRED
;
857 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record is not valid yet, prohibiting access.");
858 return PAM_ACCT_EXPIRED
;
861 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record is not valid anymore, prohibiting access.");
862 return PAM_ACCT_EXPIRED
;
866 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record not valid, prohibiting access.");
867 return PAM_ACCT_EXPIRED
;
873 t
= user_record_ratelimit_next_try(ur
);
874 if (t
!= USEC_INFINITY
) {
875 usec_t n
= now(CLOCK_REALTIME
);
878 char buf
[FORMAT_TIMESPAN_MAX
];
879 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Too many logins, try again in %s.",
880 format_timespan(buf
, sizeof(buf
), t
- n
, USEC_PER_SEC
));
886 r
= user_record_test_password_change_required(ur
);
890 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password change required.");
891 return PAM_NEW_AUTHTOK_REQD
;
894 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password expired, change requird.");
895 return PAM_NEW_AUTHTOK_REQD
;
898 /* Strictly speaking this is only about password expiration, and we might want to allow
899 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
900 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password is expired, but can't change, refusing login.");
901 return PAM_AUTHTOK_EXPIRED
;
904 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "Password will expire soon, please change.");
908 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
913 (void) pam_prompt(handle
, PAM_ERROR_MSG
, NULL
, "User record not valid, prohibiting access.");
914 return PAM_AUTHTOK_EXPIRED
;
923 _public_ PAM_EXTERN
int pam_sm_chauthtok(
924 pam_handle_t
*handle
,
929 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
, *old_secret
= NULL
, *new_secret
= NULL
;
930 const char *old_password
= NULL
, *new_password
= NULL
;
931 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
932 unsigned n_attempts
= 0;
936 if (parse_argv(handle
,
943 pam_syslog(handle
, LOG_DEBUG
, "pam-systemd-homed account management");
945 r
= pam_acquire_bus_connection(handle
, &bus
);
946 if (r
!= PAM_SUCCESS
)
949 r
= acquire_user_record(handle
, NULL
, &ur
);
950 if (r
!= PAM_SUCCESS
)
953 /* Start with cached credentials */
954 r
= pam_get_item(handle
, PAM_OLDAUTHTOK
, (const void**) &old_password
);
955 if (!IN_SET(r
, PAM_BAD_ITEM
, PAM_SUCCESS
)) {
956 pam_syslog(handle
, LOG_ERR
, "Failed to get old password: %s", pam_strerror(handle
, r
));
959 r
= pam_get_item(handle
, PAM_AUTHTOK
, (const void**) &new_password
);
960 if (!IN_SET(r
, PAM_BAD_ITEM
, PAM_SUCCESS
)) {
961 pam_syslog(handle
, LOG_ERR
, "Failed to get cached password: %s", pam_strerror(handle
, r
));
965 if (isempty(new_password
)) {
966 /* No, it's not cached, then let's ask for the password and its verification, and cache
969 r
= pam_get_authtok_noverify(handle
, &new_password
, "New password: ");
970 if (r
!= PAM_SUCCESS
) {
971 pam_syslog(handle
, LOG_ERR
, "Failed to get new password: %s", pam_strerror(handle
, r
));
974 if (isempty(new_password
)) {
975 pam_syslog(handle
, LOG_DEBUG
, "Password request aborted.");
976 return PAM_AUTHTOK_ERR
;
979 r
= pam_get_authtok_verify(handle
, &new_password
, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
980 if (r
!= PAM_SUCCESS
) {
981 pam_syslog(handle
, LOG_ERR
, "Failed to get password again: %s", pam_strerror(handle
, r
));
985 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
986 // that, and instead assume the password was already verified once when it is found to be
987 // cached already. needs to be fixed in pam_pwquality
990 /* Now everything is cached and checked, let's exit from the preliminary check */
991 if (FLAGS_SET(flags
, PAM_PRELIM_CHECK
))
994 old_secret
= user_record_new();
996 return pam_log_oom(handle
);
998 if (!isempty(old_password
)) {
999 r
= user_record_set_password(old_secret
, STRV_MAKE(old_password
), true);
1001 pam_syslog(handle
, LOG_ERR
, "Failed to store old password: %s", strerror_safe(r
));
1002 return PAM_SERVICE_ERR
;
1006 new_secret
= user_record_new();
1008 return pam_log_oom(handle
);
1010 r
= user_record_set_password(new_secret
, STRV_MAKE(new_password
), true);
1012 pam_syslog(handle
, LOG_ERR
, "Failed to store new password: %s", strerror_safe(r
));
1013 return PAM_SERVICE_ERR
;
1017 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1018 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1020 r
= bus_message_new_method_call(bus
, &m
, bus_home_mgr
, "ChangePasswordHome");
1022 return pam_bus_log_create_error(handle
, r
);
1024 r
= sd_bus_message_append(m
, "s", ur
->user_name
);
1026 return pam_bus_log_create_error(handle
, r
);
1028 r
= bus_message_append_secret(m
, new_secret
);
1030 return pam_bus_log_create_error(handle
, r
);
1032 r
= bus_message_append_secret(m
, old_secret
);
1034 return pam_bus_log_create_error(handle
, r
);
1036 r
= sd_bus_call(bus
, m
, HOME_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, NULL
);
1038 r
= handle_generic_user_record_error(handle
, ur
->user_name
, old_secret
, r
, &error
);
1039 if (r
== PAM_CONV_ERR
) {
1040 pam_syslog(handle
, LOG_ERR
, "Failed to prompt for password/prompt.");
1041 return PAM_CONV_ERR
;
1043 if (r
!= PAM_SUCCESS
)
1046 pam_syslog(handle
, LOG_NOTICE
, "Successfully changed password for user %s.", ur
->user_name
);
1050 if (++n_attempts
>= 5)
1056 pam_syslog(handle
, LOG_NOTICE
, "Failed to change password for user %s: %m", ur
->user_name
);
1057 return PAM_MAXTRIES
;