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