]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/pam_systemd_home.c
tree: wide "the the" and other trivial grammar fixes
[thirdparty/systemd.git] / src / home / pam_systemd_home.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "errno-util.h"
11 #include "fd-util.h"
12 #include "home-util.h"
13 #include "memory-util.h"
14 #include "pam-util.h"
15 #include "parse-util.h"
16 #include "strv.h"
17 #include "user-record-util.h"
18 #include "user-record.h"
19 #include "user-util.h"
20
21 static int parse_argv(
22 pam_handle_t *handle,
23 int argc, const char **argv,
24 bool *please_suspend,
25 bool *debug) {
26
27 int i;
28
29 assert(argc >= 0);
30 assert(argc == 0 || argv);
31
32 for (i = 0; i < argc; i++) {
33 const char *v;
34
35 if ((v = startswith(argv[i], "suspend="))) {
36 int k;
37
38 k = parse_boolean(v);
39 if (k < 0)
40 pam_syslog(handle, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v);
41 else if (please_suspend)
42 *please_suspend = k;
43
44 } else if (streq(argv[i], "debug")) {
45 if (debug)
46 *debug = true;
47
48 } else if ((v = startswith(argv[i], "debug="))) {
49 int k;
50 k = parse_boolean(v);
51 if (k < 0)
52 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
53 else if (debug)
54 *debug = k;
55
56 } else
57 pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
58 }
59
60 return 0;
61 }
62
63 static int parse_env(
64 pam_handle_t *handle,
65 bool *please_suspend) {
66
67 const char *v;
68 int r;
69
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 */
73
74 v = pam_getenv(handle, "SYSTEMD_HOME_SUSPEND");
75 if (!v) {
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");
79 if (!v)
80 return 0;
81 }
82
83 r = parse_boolean(v);
84 if (r < 0)
85 pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v);
86 else if (please_suspend)
87 *please_suspend = r;
88
89 return 0;
90 }
91
92 static int acquire_user_record(
93 pam_handle_t *handle,
94 const char *username,
95 UserRecord **ret_record) {
96
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;
103 int r;
104
105 assert(handle);
106
107 if (!username) {
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));
111 return r;
112 }
113
114 if (isempty(username)) {
115 pam_syslog(handle, LOG_ERR, "User name not set.");
116 return PAM_SERVICE_ERR;
117 }
118 }
119
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;
124
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);
132 if (!homed_field)
133 return pam_log_oom(handle);
134
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));
139 return r;
140 }
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;
146 } else {
147 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
148 _cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
149
150 r = pam_acquire_bus_connection(handle, &bus);
151 if (r != PAM_SUCCESS)
152 return r;
153
154 r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
155 if (r < 0) {
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));
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);
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, (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));
242
243 return PAM_USER_UNKNOWN;
244 }
245
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
272 static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
273 safe_close(PTR_TO_FD(data));
274 }
275
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(secret);
285 assert(error);
286
287 int r;
288
289 /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
290
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;
295
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));
299 return PAM_MAXTRIES;
300
301 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
302 _cleanup_(erase_and_freep) char *newp = NULL;
303
304 /* This didn't work? Ask for an (additional?) password */
305
306 if (strv_isempty(secret->password))
307 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password: ");
308 else {
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: ");
311 }
312 if (r != PAM_SUCCESS)
313 return PAM_CONV_ERR; /* no logging here */
314
315 if (isempty(newp)) {
316 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
317 return PAM_AUTHTOK_ERR;
318 }
319
320 r = user_record_set_password(secret, STRV_MAKE(newp), true);
321 if (r < 0) {
322 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
323 return PAM_SERVICE_ERR;
324 }
325
326 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
327 _cleanup_(erase_and_freep) char *newp = NULL;
328
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: ");
332 } else {
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: ");
335 }
336 if (r != PAM_SUCCESS)
337 return PAM_CONV_ERR; /* no logging here */
338
339 if (isempty(newp)) {
340 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
341 return PAM_AUTHTOK_ERR;
342 }
343
344 r = user_record_set_password(secret, STRV_MAKE(newp), true);
345 if (r < 0) {
346 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
347 return PAM_SERVICE_ERR;
348 }
349
350 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
351 _cleanup_(erase_and_freep) char *newp = NULL;
352
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 */
356
357 if (isempty(newp)) {
358 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
359 return PAM_AUTHTOK_ERR;
360 }
361
362 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
363 if (r < 0) {
364 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
365 return PAM_SERVICE_ERR;
366 }
367
368 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
369
370 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please authenticate physically on security token of user %s.", user_name);
371
372 r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
373 if (r < 0) {
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;
376 }
377
378 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
379 _cleanup_(erase_and_freep) char *newp = NULL;
380
381 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN incorrect for user %s.", user_name);
382 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Sorry, retry security token PIN: ");
383 if (r != PAM_SUCCESS)
384 return PAM_CONV_ERR; /* no logging here */
385
386 if (isempty(newp)) {
387 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
388 return PAM_AUTHTOK_ERR;
389 }
390
391 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
392 if (r < 0) {
393 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
394 return PAM_SERVICE_ERR;
395 }
396
397 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
398 _cleanup_(erase_and_freep) char *newp = NULL;
399
400 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN of user %s incorrect (only a few tries left!)", user_name);
401 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Sorry, retry security token PIN: ");
402 if (r != PAM_SUCCESS)
403 return PAM_CONV_ERR; /* no logging here */
404
405 if (isempty(newp)) {
406 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
407 return PAM_AUTHTOK_ERR;
408 }
409
410 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
411 if (r < 0) {
412 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
413 return PAM_SERVICE_ERR;
414 }
415
416 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
417 _cleanup_(erase_and_freep) char *newp = NULL;
418
419 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN of user %s incorrect (only one try left!)", user_name);
420 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Sorry, retry security token PIN: ");
421 if (r != PAM_SUCCESS)
422 return PAM_CONV_ERR; /* no logging here */
423
424 if (isempty(newp)) {
425 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
426 return PAM_AUTHTOK_ERR;
427 }
428
429 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
430 if (r < 0) {
431 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
432 return PAM_SERVICE_ERR;
433 }
434
435 } else {
436 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
437 return PAM_SERVICE_ERR;
438 }
439
440 return PAM_SUCCESS;
441 }
442
443 static int acquire_home(
444 pam_handle_t *handle,
445 bool please_authenticate,
446 bool please_suspend,
447 bool debug) {
448
449 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
450 bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
451 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
452 _cleanup_close_ int acquired_fd = -1;
453 _cleanup_free_ char *fd_field = NULL;
454 const void *home_fd_ptr = NULL;
455 const char *username = NULL;
456 unsigned n_attempts = 0;
457 int r;
458
459 assert(handle);
460
461 /* This acquires a reference to a home directory in one of two ways: if please_authenticate is true,
462 * then we'll call AcquireHome() after asking the user for a password. Otherwise it tries to call
463 * RefHome() and if that fails queries the user for a password and uses AcquireHome().
464 *
465 * The idea is that the PAM authentication hook sets please_authenticate and thus always
466 * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
467 * authentication if possible, but with authentication if necessary. */
468
469 r = pam_get_user(handle, &username, NULL);
470 if (r != PAM_SUCCESS) {
471 pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
472 return r;
473 }
474
475 if (isempty(username)) {
476 pam_syslog(handle, LOG_ERR, "User name not set.");
477 return PAM_SERVICE_ERR;
478 }
479
480 /* If we already have acquired the fd, let's shortcut this */
481 fd_field = strjoin("systemd-home-fd-", username);
482 if (!fd_field)
483 return pam_log_oom(handle);
484
485 r = pam_get_data(handle, fd_field, &home_fd_ptr);
486 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
487 pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
488 return r;
489 }
490 if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
491 return PAM_SUCCESS;
492
493 r = pam_acquire_bus_connection(handle, &bus);
494 if (r != PAM_SUCCESS)
495 return r;
496
497 r = acquire_user_record(handle, username, &ur);
498 if (r != PAM_SUCCESS)
499 return r;
500
501 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
502 * might happen that the record we stored on the host does not match the encryption password of
503 * the LUKS image in case the image was used in a different system where the password was
504 * changed. In that case it will happen that the LUKS password and the host password are
505 * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
506 * treat bad passwords as a request to collect one more password and pass the new all all previously
507 * used passwords again. */
508
509 for (;;) {
510 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
511 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
512
513 if (do_auth && !secret) {
514 const char *cached_password = NULL;
515
516 secret = user_record_new();
517 if (!secret)
518 return pam_log_oom(handle);
519
520 /* If there's already a cached password, use it. But if not let's authenticate
521 * without anything, maybe some other authentication mechanism systemd-homed
522 * implements (such as PKCS#11) allows us to authenticate without anything else. */
523 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &cached_password);
524 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
525 pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
526 return r;
527 }
528
529 if (!isempty(cached_password)) {
530 r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
531 if (r < 0) {
532 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
533 return PAM_SERVICE_ERR;
534 }
535 }
536 }
537
538 r = bus_message_new_method_call(bus, &m, bus_home_mgr, do_auth ? "AcquireHome" : "RefHome");
539 if (r < 0)
540 return pam_bus_log_create_error(handle, r);
541
542 r = sd_bus_message_append(m, "s", ur->user_name);
543 if (r < 0)
544 return pam_bus_log_create_error(handle, r);
545
546 if (do_auth) {
547 r = bus_message_append_secret(m, secret);
548 if (r < 0)
549 return pam_bus_log_create_error(handle, r);
550 }
551
552 r = sd_bus_message_append(m, "b", please_suspend);
553 if (r < 0)
554 return pam_bus_log_create_error(handle, r);
555
556 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
557 if (r < 0) {
558
559 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_NOT_ACTIVE))
560 /* Only on RefHome(): We can't access the home directory currently, unless
561 * it's unlocked with a password. Hence, let's try this again, this time with
562 * authentication. */
563 home_not_active = true;
564 else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
565 home_locked = true; /* Similar */
566 else {
567 r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error);
568 if (r == PAM_CONV_ERR) {
569 /* Password/PIN prompts will fail in certain environments, for example when
570 * we are called from OpenSSH's account or session hooks, or in systemd's
571 * per-service PAM logic. In that case, print a friendly message and accept
572 * failure. */
573
574 if (home_not_active)
575 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently not active, please log in locally first.", ur->user_name);
576 if (home_locked)
577 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
578
579 pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
580
581 return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
582 }
583 if (r != PAM_SUCCESS)
584 return r;
585 }
586
587 } else {
588 int fd;
589
590 r = sd_bus_message_read(reply, "h", &fd);
591 if (r < 0)
592 return pam_bus_log_parse_error(handle, r);
593
594 acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
595 if (acquired_fd < 0) {
596 pam_syslog(handle, LOG_ERR, "Failed to duplicate acquired fd: %s", bus_error_message(&error, r));
597 return PAM_SERVICE_ERR;
598 }
599
600 break;
601 }
602
603 if (++n_attempts >= 5) {
604 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many unsuccessful login attempts for user %s, refusing.", ur->user_name);
605 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
606 return PAM_MAXTRIES;
607 }
608
609 /* Try again, this time with authentication if we didn't do that before. */
610 do_auth = true;
611 }
612
613 r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
614 if (r < 0) {
615 pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
616 return r;
617 }
618 TAKE_FD(acquired_fd);
619
620 if (do_auth) {
621 /* We likely just activated the home directory, let's flush out the user record, since a
622 * newer embedded user record might have been acquired from the activation. */
623
624 r = release_user_record(handle, ur->user_name);
625 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
626 return r;
627 }
628
629 pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
630
631 return PAM_SUCCESS;
632 }
633
634 static int release_home_fd(pam_handle_t *handle, const char *username) {
635 _cleanup_free_ char *fd_field = NULL;
636 const void *home_fd_ptr = NULL;
637 int r;
638
639 assert(handle);
640 assert(username);
641
642 fd_field = strjoin("systemd-home-fd-", username);
643 if (!fd_field)
644 return pam_log_oom(handle);
645
646 r = pam_get_data(handle, fd_field, &home_fd_ptr);
647 if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0))
648 return PAM_NO_MODULE_DATA;
649 if (r != PAM_SUCCESS) {
650 pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
651 return r;
652 }
653
654 r = pam_set_data(handle, fd_field, NULL, NULL);
655 if (r != PAM_SUCCESS)
656 pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
657
658 return r;
659 }
660
661 _public_ PAM_EXTERN int pam_sm_authenticate(
662 pam_handle_t *handle,
663 int flags,
664 int argc, const char **argv) {
665
666 bool debug = false, suspend_please = false;
667
668 if (parse_env(handle, &suspend_please) < 0)
669 return PAM_AUTH_ERR;
670
671 if (parse_argv(handle,
672 argc, argv,
673 &suspend_please,
674 &debug) < 0)
675 return PAM_AUTH_ERR;
676
677 if (debug)
678 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed authenticating");
679
680 return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug);
681 }
682
683 _public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
684 return PAM_SUCCESS;
685 }
686
687 _public_ PAM_EXTERN int pam_sm_open_session(
688 pam_handle_t *handle,
689 int flags,
690 int argc, const char **argv) {
691
692 bool debug = false, suspend_please = false;
693 int r;
694
695 if (parse_env(handle, &suspend_please) < 0)
696 return PAM_SESSION_ERR;
697
698 if (parse_argv(handle,
699 argc, argv,
700 &suspend_please,
701 &debug) < 0)
702 return PAM_SESSION_ERR;
703
704 if (debug)
705 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session start");
706
707 r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug);
708 if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
709 return PAM_SUCCESS;
710 if (r != PAM_SUCCESS)
711 return r;
712
713 r = pam_putenv(handle, "SYSTEMD_HOME=1");
714 if (r != PAM_SUCCESS) {
715 pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME: %s", pam_strerror(handle, r));
716 return r;
717 }
718
719 r = pam_putenv(handle, suspend_please ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
720 if (r != PAM_SUCCESS) {
721 pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: %s", pam_strerror(handle, r));
722 return r;
723 }
724
725 /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
726 * not going to process the bus connection in that time, so let's better close before the daemon
727 * kicks us off because we are not processing anything. */
728 (void) pam_release_bus_connection(handle);
729 return PAM_SUCCESS;
730 }
731
732 _public_ PAM_EXTERN int pam_sm_close_session(
733 pam_handle_t *handle,
734 int flags,
735 int argc, const char **argv) {
736
737 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
738 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
739 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
740 const char *username = NULL;
741 bool debug = false;
742 int r;
743
744 if (parse_argv(handle,
745 argc, argv,
746 NULL,
747 &debug) < 0)
748 return PAM_SESSION_ERR;
749
750 if (debug)
751 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
752
753 r = pam_get_user(handle, &username, NULL);
754 if (r != PAM_SUCCESS) {
755 pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
756 return r;
757 }
758
759 if (isempty(username)) {
760 pam_syslog(handle, LOG_ERR, "User name not set.");
761 return PAM_SERVICE_ERR;
762 }
763
764 /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
765 * call will be able to do its thing. */
766 r = release_home_fd(handle, username);
767 if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
768 return PAM_SUCCESS;
769 if (r != PAM_SUCCESS)
770 return r;
771
772 r = pam_acquire_bus_connection(handle, &bus);
773 if (r != PAM_SUCCESS)
774 return r;
775
776 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
777 if (r < 0)
778 return pam_bus_log_create_error(handle, r);
779
780 r = sd_bus_message_append(m, "s", username);
781 if (r < 0)
782 return pam_bus_log_create_error(handle, r);
783
784 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
785 if (r < 0) {
786 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
787 pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
788 else {
789 pam_syslog(handle, LOG_ERR, "Failed to release user home: %s", bus_error_message(&error, r));
790 return PAM_SESSION_ERR;
791 }
792 }
793
794 return PAM_SUCCESS;
795 }
796
797 _public_ PAM_EXTERN int pam_sm_acct_mgmt(
798 pam_handle_t *handle,
799 int flags,
800 int argc,
801 const char **argv) {
802
803 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
804 bool debug = false, please_suspend = false;
805 usec_t t;
806 int r;
807
808 if (parse_env(handle, &please_suspend) < 0)
809 return PAM_AUTH_ERR;
810
811 if (parse_argv(handle,
812 argc, argv,
813 &please_suspend,
814 &debug) < 0)
815 return PAM_AUTH_ERR;
816
817 if (debug)
818 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
819
820 r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug);
821 if (r == PAM_USER_UNKNOWN)
822 return PAM_SUCCESS; /* we don't have anything to say about users we don't manage */
823 if (r != PAM_SUCCESS)
824 return r;
825
826 r = acquire_user_record(handle, NULL, &ur);
827 if (r != PAM_SUCCESS)
828 return r;
829
830 r = user_record_test_blocked(ur);
831 switch (r) {
832
833 case -ESTALE:
834 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access.");
835 return PAM_ACCT_EXPIRED;
836
837 case -ENOLCK:
838 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
839 return PAM_ACCT_EXPIRED;
840
841 case -EL2HLT:
842 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid yet, prohibiting access.");
843 return PAM_ACCT_EXPIRED;
844
845 case -EL3HLT:
846 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid anymore, prohibiting access.");
847 return PAM_ACCT_EXPIRED;
848
849 default:
850 if (r < 0) {
851 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
852 return PAM_ACCT_EXPIRED;
853 }
854
855 break;
856 }
857
858 t = user_record_ratelimit_next_try(ur);
859 if (t != USEC_INFINITY) {
860 usec_t n = now(CLOCK_REALTIME);
861
862 if (t > n) {
863 char buf[FORMAT_TIMESPAN_MAX];
864 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many logins, try again in %s.",
865 format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
866
867 return PAM_MAXTRIES;
868 }
869 }
870
871 r = user_record_test_password_change_required(ur);
872 switch (r) {
873
874 case -EKEYREVOKED:
875 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password change required.");
876 return PAM_NEW_AUTHTOK_REQD;
877
878 case -EOWNERDEAD:
879 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password expired, change requird.");
880 return PAM_NEW_AUTHTOK_REQD;
881
882 case -EKEYREJECTED:
883 /* Strictly speaking this is only about password expiration, and we might want to allow
884 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
885 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password is expired, but can't change, refusing login.");
886 return PAM_AUTHTOK_EXPIRED;
887
888 case -EKEYEXPIRED:
889 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
890 break;
891
892 case -EROFS:
893 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
894 break;
895
896 default:
897 if (r < 0) {
898 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
899 return PAM_AUTHTOK_EXPIRED;
900 }
901
902 break;
903 }
904
905 return PAM_SUCCESS;
906 }
907
908 _public_ PAM_EXTERN int pam_sm_chauthtok(
909 pam_handle_t *handle,
910 int flags,
911 int argc,
912 const char **argv) {
913
914 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
915 const char *old_password = NULL, *new_password = NULL;
916 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
917 unsigned n_attempts = 0;
918 bool debug = false;
919 int r;
920
921 if (parse_argv(handle,
922 argc, argv,
923 NULL,
924 &debug) < 0)
925 return PAM_AUTH_ERR;
926
927 if (debug)
928 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
929
930 r = pam_acquire_bus_connection(handle, &bus);
931 if (r != PAM_SUCCESS)
932 return r;
933
934 r = acquire_user_record(handle, NULL, &ur);
935 if (r != PAM_SUCCESS)
936 return r;
937
938 /* Start with cached credentials */
939 r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
940 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
941 pam_syslog(handle, LOG_ERR, "Failed to get old password: %s", pam_strerror(handle, r));
942 return r;
943 }
944 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
945 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
946 pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
947 return r;
948 }
949
950 if (isempty(new_password)) {
951 /* No, it's not cached, then let's ask for the password and its verification, and cache
952 * it. */
953
954 r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
955 if (r != PAM_SUCCESS) {
956 pam_syslog(handle, LOG_ERR, "Failed to get new password: %s", pam_strerror(handle, r));
957 return r;
958 }
959 if (isempty(new_password)) {
960 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
961 return PAM_AUTHTOK_ERR;
962 }
963
964 r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
965 if (r != PAM_SUCCESS) {
966 pam_syslog(handle, LOG_ERR, "Failed to get password again: %s", pam_strerror(handle, r));
967 return r;
968 }
969
970 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
971 // that, and instead assume the password was already verified once when it is found to be
972 // cached already. needs to be fixed in pam_pwquality
973 }
974
975 /* Now everything is cached and checked, let's exit from the preliminary check */
976 if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
977 return PAM_SUCCESS;
978
979 old_secret = user_record_new();
980 if (!old_secret)
981 return pam_log_oom(handle);
982
983 if (!isempty(old_password)) {
984 r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
985 if (r < 0) {
986 pam_syslog(handle, LOG_ERR, "Failed to store old password: %s", strerror_safe(r));
987 return PAM_SERVICE_ERR;
988 }
989 }
990
991 new_secret = user_record_new();
992 if (!new_secret)
993 return pam_log_oom(handle);
994
995 r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
996 if (r < 0) {
997 pam_syslog(handle, LOG_ERR, "Failed to store new password: %s", strerror_safe(r));
998 return PAM_SERVICE_ERR;
999 }
1000
1001 for (;;) {
1002 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1003 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1004
1005 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
1006 if (r < 0)
1007 return pam_bus_log_create_error(handle, r);
1008
1009 r = sd_bus_message_append(m, "s", ur->user_name);
1010 if (r < 0)
1011 return pam_bus_log_create_error(handle, r);
1012
1013 r = bus_message_append_secret(m, new_secret);
1014 if (r < 0)
1015 return pam_bus_log_create_error(handle, r);
1016
1017 r = bus_message_append_secret(m, old_secret);
1018 if (r < 0)
1019 return pam_bus_log_create_error(handle, r);
1020
1021 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1022 if (r < 0) {
1023 r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
1024 if (r == PAM_CONV_ERR) {
1025 pam_syslog(handle, LOG_ERR, "Failed to prompt for password/prompt.");
1026 return PAM_CONV_ERR;
1027 }
1028 if (r != PAM_SUCCESS)
1029 return r;
1030 } else {
1031 pam_syslog(handle, LOG_NOTICE, "Successfully changed password for user %s.", ur->user_name);
1032 return PAM_SUCCESS;
1033 }
1034
1035 if (++n_attempts >= 5)
1036 break;
1037
1038 /* Try again */
1039 };
1040
1041 pam_syslog(handle, LOG_NOTICE, "Failed to change password for user %s: %m", ur->user_name);
1042 return PAM_MAXTRIES;
1043 }