]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/pam_systemd_home.c
Merge pull request #17185 from yuwata/ethtool-update
[thirdparty/systemd.git] / src / home / pam_systemd_home.c
CommitLineData
26cf9fb7
LP
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"
9b71e4ab 9#include "bus-locator.h"
73d3ac8e 10#include "bus-util.h"
26cf9fb7
LP
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
26cf9fb7
LP
22static 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
f12d19b3 36 if ((v = startswith(argv[i], "suspend="))) {
26cf9fb7
LP
37 int k;
38
39 k = parse_boolean(v);
40 if (k < 0)
80ace4f2 41 pam_syslog(handle, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v);
26cf9fb7
LP
42 else if (please_suspend)
43 *please_suspend = k;
44
332f38d0
JS
45 } else if (streq(argv[i], "debug")) {
46 if (debug)
47 *debug = true;
48
26cf9fb7
LP
49 } else if ((v = startswith(argv[i], "debug="))) {
50 int k;
26cf9fb7
LP
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
764ae4dd
LP
64static 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
26cf9fb7
LP
93static int acquire_user_record(
94 pam_handle_t *handle,
6c8428bb 95 const char *username,
26cf9fb7
LP
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;
dbe7fff4 102 _cleanup_free_ char *homed_field = NULL;
6c8428bb 103 const char *json = NULL;
26cf9fb7
LP
104 int r;
105
106 assert(handle);
107
6c8428bb
LP
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 }
26cf9fb7 114
6c8428bb
LP
115 if (isempty(username)) {
116 pam_syslog(handle, LOG_ERR, "User name not set.");
117 return PAM_SERVICE_ERR;
118 }
26cf9fb7
LP
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. */
7a8867ab 123 if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
26cf9fb7
LP
124 return PAM_USER_UNKNOWN;
125
dbe7fff4
LP
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);
26cf9fb7 138 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
dbe7fff4 139 pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
26cf9fb7 140 return r;
26cf9fb7 141 }
dbe7fff4
LP
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) */
66032ef4 145 if (json == POINTER_MAX)
dbe7fff4
LP
146 return PAM_USER_UNKNOWN;
147 } else {
26cf9fb7 148 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
dbe7fff4 149 _cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
26cf9fb7
LP
150
151 r = pam_acquire_bus_connection(handle, &bus);
152 if (r != PAM_SUCCESS)
153 return r;
154
8a1596aa 155 r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
26cf9fb7 156 if (r < 0) {
73d3ac8e 157 if (bus_error_is_unknown_service(&error)) {
26cf9fb7
LP
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
dbe7fff4
LP
175 /* First copy: for the homed-specific data field, i.e. where we know the user record is from
176 * homed */
26cf9fb7
LP
177 json_copy = strdup(json);
178 if (!json_copy)
179 return pam_log_oom(handle);
180
dbe7fff4 181 r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
26cf9fb7 182 if (r != PAM_SUCCESS) {
dbe7fff4
LP
183 pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
184 homed_field, pam_strerror(handle, r));
26cf9fb7
LP
185 return r;
186 }
187
dbe7fff4
LP
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);
26cf9fb7 198
dbe7fff4 199 r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
26cf9fb7 200 if (r != PAM_SUCCESS) {
dbe7fff4
LP
201 pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
202 homed_field, pam_strerror(handle, r));
26cf9fb7
LP
203 return r;
204 }
dbe7fff4
LP
205
206 TAKE_PTR(json_copy);
26cf9fb7
LP
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
dbe7fff4 225 /* Safety check if cached record actually matches what we are looking for */
26cf9fb7
LP
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
236user_unknown:
237 /* Cache this, so that we don't check again */
66032ef4 238 r = pam_set_data(handle, homed_field, POINTER_MAX, NULL);
26cf9fb7 239 if (r != PAM_SUCCESS)
dbe7fff4
LP
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));
26cf9fb7
LP
242
243 return PAM_USER_UNKNOWN;
244}
245
dbe7fff4
LP
246static int release_user_record(pam_handle_t *handle, const char *username) {
247 _cleanup_free_ char *homed_field = NULL, *generic_field = NULL;
26cf9fb7
LP
248 int r, k;
249
dbe7fff4
LP
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);
26cf9fb7 258 if (r != PAM_SUCCESS)
dbe7fff4
LP
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);
26cf9fb7 264
dbe7fff4 265 k = pam_set_data(handle, generic_field, NULL, NULL);
26cf9fb7 266 if (k != PAM_SUCCESS)
dbe7fff4 267 pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", generic_field, pam_strerror(handle, k));
26cf9fb7
LP
268
269 return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
270}
271
272static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
273 safe_close(PTR_TO_FD(data));
274}
275
276static 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: ");
d4232943
JS
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 }
26cf9fb7
LP
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
d4232943
JS
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 }
26cf9fb7
LP
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
d4232943 353 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN: ");
26cf9fb7
LP
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
c0bde0d2 362 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
26cf9fb7
LP
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
7b78db28
LP
378 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
379
380 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name);
381
382 r = user_record_set_fido2_user_presence_permitted(secret, true);
383 if (r < 0) {
384 pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r));
385 return PAM_SERVICE_ERR;
386 }
387
85b12944
LP
388 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
389
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;
392
26cf9fb7
LP
393 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
394 _cleanup_(erase_and_freep) char *newp = NULL;
395
d4232943
JS
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: ");
26cf9fb7
LP
398 if (r != PAM_SUCCESS)
399 return PAM_CONV_ERR; /* no logging here */
400
401 if (isempty(newp)) {
402 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
403 return PAM_AUTHTOK_ERR;
404 }
405
c0bde0d2 406 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
26cf9fb7
LP
407 if (r < 0) {
408 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
409 return PAM_SERVICE_ERR;
410 }
411
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;
414
d4232943
JS
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: ");
26cf9fb7
LP
417 if (r != PAM_SUCCESS)
418 return PAM_CONV_ERR; /* no logging here */
419
420 if (isempty(newp)) {
421 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
422 return PAM_AUTHTOK_ERR;
423 }
424
c0bde0d2 425 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
26cf9fb7
LP
426 if (r < 0) {
427 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
428 return PAM_SERVICE_ERR;
429 }
430
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;
433
d4232943
JS
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: ");
26cf9fb7
LP
436 if (r != PAM_SUCCESS)
437 return PAM_CONV_ERR; /* no logging here */
438
439 if (isempty(newp)) {
440 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
441 return PAM_AUTHTOK_ERR;
442 }
443
c0bde0d2 444 r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
26cf9fb7
LP
445 if (r < 0) {
446 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
447 return PAM_SERVICE_ERR;
448 }
449
450 } else {
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;
453 }
454
455 return PAM_SUCCESS;
456}
457
458static int acquire_home(
459 pam_handle_t *handle,
460 bool please_authenticate,
461 bool please_suspend,
462 bool debug) {
463
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;
6c8428bb 468 _cleanup_free_ char *fd_field = NULL;
26cf9fb7 469 const void *home_fd_ptr = NULL;
6c8428bb 470 const char *username = NULL;
26cf9fb7
LP
471 unsigned n_attempts = 0;
472 int r;
473
474 assert(handle);
475
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().
479 *
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. */
483
6c8428bb
LP
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));
487 return r;
488 }
489
490 if (isempty(username)) {
491 pam_syslog(handle, LOG_ERR, "User name not set.");
492 return PAM_SERVICE_ERR;
493 }
494
26cf9fb7 495 /* If we already have acquired the fd, let's shortcut this */
6c8428bb
LP
496 fd_field = strjoin("systemd-home-fd-", username);
497 if (!fd_field)
498 return pam_log_oom(handle);
499
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));
503 return r;
504 }
da4340fd 505 if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
26cf9fb7
LP
506 return PAM_SUCCESS;
507
508 r = pam_acquire_bus_connection(handle, &bus);
509 if (r != PAM_SUCCESS)
510 return r;
511
6c8428bb 512 r = acquire_user_record(handle, username, &ur);
26cf9fb7
LP
513 if (r != PAM_SUCCESS)
514 return r;
515
516 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
37b22b3b 517 * might happen that the record we stored on the host does not match the encryption password of
26cf9fb7
LP
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. */
523
524 for (;;) {
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;
527
528 if (do_auth && !secret) {
529 const char *cached_password = NULL;
530
531 secret = user_record_new();
532 if (!secret)
533 return pam_log_oom(handle);
534
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));
541 return r;
542 }
543
544 if (!isempty(cached_password)) {
545 r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
546 if (r < 0) {
547 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
548 return PAM_SERVICE_ERR;
549 }
550 }
551 }
552
8a1596aa 553 r = bus_message_new_method_call(bus, &m, bus_home_mgr, do_auth ? "AcquireHome" : "RefHome");
26cf9fb7
LP
554 if (r < 0)
555 return pam_bus_log_create_error(handle, r);
556
557 r = sd_bus_message_append(m, "s", ur->user_name);
558 if (r < 0)
559 return pam_bus_log_create_error(handle, r);
560
561 if (do_auth) {
562 r = bus_message_append_secret(m, secret);
563 if (r < 0)
564 return pam_bus_log_create_error(handle, r);
565 }
566
567 r = sd_bus_message_append(m, "b", please_suspend);
568 if (r < 0)
569 return pam_bus_log_create_error(handle, r);
570
571 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
572 if (r < 0) {
573
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
577 * authentication. */
578 home_not_active = true;
579 else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
580 home_locked = true; /* Similar */
581 else {
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
587 * failure. */
588
589 if (home_not_active)
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);
591 if (home_locked)
592 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
593
594 pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
595
596 return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
597 }
598 if (r != PAM_SUCCESS)
599 return r;
600 }
601
602 } else {
603 int fd;
604
605 r = sd_bus_message_read(reply, "h", &fd);
606 if (r < 0)
607 return pam_bus_log_parse_error(handle, r);
608
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;
613 }
614
615 break;
616 }
617
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));
621 return PAM_MAXTRIES;
622 }
623
624 /* Try again, this time with authentication if we didn't do that before. */
625 do_auth = true;
626 }
627
6c8428bb 628 r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
26cf9fb7
LP
629 if (r < 0) {
630 pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
631 return r;
632 }
633 TAKE_FD(acquired_fd);
634
635 if (do_auth) {
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. */
638
dbe7fff4 639 r = release_user_record(handle, ur->user_name);
26cf9fb7
LP
640 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
641 return r;
642 }
643
644 pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
645
646 return PAM_SUCCESS;
647}
648
6c8428bb
LP
649static int release_home_fd(pam_handle_t *handle, const char *username) {
650 _cleanup_free_ char *fd_field = NULL;
26cf9fb7
LP
651 const void *home_fd_ptr = NULL;
652 int r;
653
6c8428bb
LP
654 assert(handle);
655 assert(username);
656
657 fd_field = strjoin("systemd-home-fd-", username);
658 if (!fd_field)
659 return pam_log_oom(handle);
660
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))
26cf9fb7 663 return PAM_NO_MODULE_DATA;
6c8428bb
LP
664 if (r != PAM_SUCCESS) {
665 pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
666 return r;
667 }
26cf9fb7 668
6c8428bb 669 r = pam_set_data(handle, fd_field, NULL, NULL);
26cf9fb7
LP
670 if (r != PAM_SUCCESS)
671 pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
672
673 return r;
674}
675
676_public_ PAM_EXTERN int pam_sm_authenticate(
677 pam_handle_t *handle,
678 int flags,
679 int argc, const char **argv) {
680
681 bool debug = false, suspend_please = false;
682
764ae4dd
LP
683 if (parse_env(handle, &suspend_please) < 0)
684 return PAM_AUTH_ERR;
685
26cf9fb7
LP
686 if (parse_argv(handle,
687 argc, argv,
688 &suspend_please,
689 &debug) < 0)
690 return PAM_AUTH_ERR;
691
692 if (debug)
693 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed authenticating");
694
695 return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug);
696}
697
698_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
699 return PAM_SUCCESS;
700}
701
702_public_ PAM_EXTERN int pam_sm_open_session(
703 pam_handle_t *handle,
704 int flags,
705 int argc, const char **argv) {
706
707 bool debug = false, suspend_please = false;
708 int r;
709
764ae4dd
LP
710 if (parse_env(handle, &suspend_please) < 0)
711 return PAM_SESSION_ERR;
712
26cf9fb7
LP
713 if (parse_argv(handle,
714 argc, argv,
715 &suspend_please,
716 &debug) < 0)
717 return PAM_SESSION_ERR;
718
719 if (debug)
720 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session start");
721
722 r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug);
723 if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
724 return PAM_SUCCESS;
725 if (r != PAM_SUCCESS)
726 return r;
727
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));
731 return r;
732 }
733
764ae4dd
LP
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));
737 return r;
738 }
739
26cf9fb7
LP
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);
744 return PAM_SUCCESS;
745}
746
747_public_ PAM_EXTERN int pam_sm_close_session(
748 pam_handle_t *handle,
749 int flags,
750 int argc, const char **argv) {
751
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;
756 bool debug = false;
757 int r;
758
759 if (parse_argv(handle,
760 argc, argv,
761 NULL,
762 &debug) < 0)
763 return PAM_SESSION_ERR;
764
765 if (debug)
766 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
767
6c8428bb
LP
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));
771 return r;
772 }
773
774 if (isempty(username)) {
775 pam_syslog(handle, LOG_ERR, "User name not set.");
776 return PAM_SERVICE_ERR;
777 }
778
26cf9fb7
LP
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. */
6c8428bb 781 r = release_home_fd(handle, username);
26cf9fb7
LP
782 if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
783 return PAM_SUCCESS;
784 if (r != PAM_SUCCESS)
785 return r;
786
26cf9fb7
LP
787 r = pam_acquire_bus_connection(handle, &bus);
788 if (r != PAM_SUCCESS)
789 return r;
790
8a1596aa 791 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
26cf9fb7
LP
792 if (r < 0)
793 return pam_bus_log_create_error(handle, r);
794
795 r = sd_bus_message_append(m, "s", username);
796 if (r < 0)
797 return pam_bus_log_create_error(handle, r);
798
799 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
800 if (r < 0) {
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);
803 else {
804 pam_syslog(handle, LOG_ERR, "Failed to release user home: %s", bus_error_message(&error, r));
805 return PAM_SESSION_ERR;
806 }
807 }
808
809 return PAM_SUCCESS;
810}
811
812_public_ PAM_EXTERN int pam_sm_acct_mgmt(
813 pam_handle_t *handle,
814 int flags,
815 int argc,
816 const char **argv) {
817
818 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
819 bool debug = false, please_suspend = false;
820 usec_t t;
821 int r;
822
764ae4dd
LP
823 if (parse_env(handle, &please_suspend) < 0)
824 return PAM_AUTH_ERR;
825
26cf9fb7
LP
826 if (parse_argv(handle,
827 argc, argv,
828 &please_suspend,
829 &debug) < 0)
830 return PAM_AUTH_ERR;
831
832 if (debug)
833 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
834
835 r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug);
26cf9fb7
LP
836 if (r != PAM_SUCCESS)
837 return r;
838
6c8428bb 839 r = acquire_user_record(handle, NULL, &ur);
26cf9fb7
LP
840 if (r != PAM_SUCCESS)
841 return r;
842
843 r = user_record_test_blocked(ur);
844 switch (r) {
845
846 case -ESTALE:
51a95db6
LP
847 pam_syslog(handle, LOG_WARNING, "User record for '%s' is newer than current system time, assuming incorrect system clock, allowing access.", ur->user_name);
848 break;
26cf9fb7
LP
849
850 case -ENOLCK:
851 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
852 return PAM_ACCT_EXPIRED;
853
854 case -EL2HLT:
855 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid yet, prohibiting access.");
856 return PAM_ACCT_EXPIRED;
857
858 case -EL3HLT:
859 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid anymore, prohibiting access.");
860 return PAM_ACCT_EXPIRED;
861
862 default:
863 if (r < 0) {
864 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
865 return PAM_ACCT_EXPIRED;
866 }
867
868 break;
869 }
870
871 t = user_record_ratelimit_next_try(ur);
872 if (t != USEC_INFINITY) {
873 usec_t n = now(CLOCK_REALTIME);
874
875 if (t > n) {
876 char buf[FORMAT_TIMESPAN_MAX];
877 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many logins, try again in %s.",
878 format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
879
880 return PAM_MAXTRIES;
881 }
882 }
883
884 r = user_record_test_password_change_required(ur);
885 switch (r) {
886
887 case -EKEYREVOKED:
888 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password change required.");
889 return PAM_NEW_AUTHTOK_REQD;
890
891 case -EOWNERDEAD:
892 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password expired, change requird.");
893 return PAM_NEW_AUTHTOK_REQD;
894
895 case -EKEYREJECTED:
896 /* Strictly speaking this is only about password expiration, and we might want to allow
897 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
898 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password is expired, but can't change, refusing login.");
899 return PAM_AUTHTOK_EXPIRED;
900
901 case -EKEYEXPIRED:
902 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
903 break;
3e0b5486
LP
904
905 case -ESTALE:
906 /* If the system clock is wrong, let's log but continue */
907 pam_syslog(handle, LOG_WARNING, "Couldn't check if password change is required, last change is in the future, system clock likely wrong.");
908 break;
26cf9fb7
LP
909
910 case -EROFS:
911 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
912 break;
913
914 default:
915 if (r < 0) {
916 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
917 return PAM_AUTHTOK_EXPIRED;
918 }
919
920 break;
921 }
922
923 return PAM_SUCCESS;
924}
925
926_public_ PAM_EXTERN int pam_sm_chauthtok(
927 pam_handle_t *handle,
928 int flags,
929 int argc,
930 const char **argv) {
931
932 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
933 const char *old_password = NULL, *new_password = NULL;
934 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
935 unsigned n_attempts = 0;
936 bool debug = false;
937 int r;
938
939 if (parse_argv(handle,
940 argc, argv,
941 NULL,
942 &debug) < 0)
943 return PAM_AUTH_ERR;
944
945 if (debug)
946 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
947
948 r = pam_acquire_bus_connection(handle, &bus);
949 if (r != PAM_SUCCESS)
950 return r;
951
6c8428bb 952 r = acquire_user_record(handle, NULL, &ur);
26cf9fb7
LP
953 if (r != PAM_SUCCESS)
954 return r;
955
956 /* Start with cached credentials */
957 r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
958 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
959 pam_syslog(handle, LOG_ERR, "Failed to get old password: %s", pam_strerror(handle, r));
960 return r;
961 }
962 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
963 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
964 pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
965 return r;
966 }
967
968 if (isempty(new_password)) {
969 /* No, it's not cached, then let's ask for the password and its verification, and cache
970 * it. */
971
972 r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
973 if (r != PAM_SUCCESS) {
974 pam_syslog(handle, LOG_ERR, "Failed to get new password: %s", pam_strerror(handle, r));
975 return r;
976 }
977 if (isempty(new_password)) {
978 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
979 return PAM_AUTHTOK_ERR;
980 }
981
982 r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
983 if (r != PAM_SUCCESS) {
984 pam_syslog(handle, LOG_ERR, "Failed to get password again: %s", pam_strerror(handle, r));
985 return r;
986 }
987
988 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
989 // that, and instead assume the password was already verified once when it is found to be
990 // cached already. needs to be fixed in pam_pwquality
991 }
992
993 /* Now everything is cached and checked, let's exit from the preliminary check */
994 if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
995 return PAM_SUCCESS;
996
26cf9fb7
LP
997 old_secret = user_record_new();
998 if (!old_secret)
999 return pam_log_oom(handle);
1000
1001 if (!isempty(old_password)) {
1002 r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
1003 if (r < 0) {
1004 pam_syslog(handle, LOG_ERR, "Failed to store old password: %s", strerror_safe(r));
1005 return PAM_SERVICE_ERR;
1006 }
1007 }
1008
1009 new_secret = user_record_new();
1010 if (!new_secret)
1011 return pam_log_oom(handle);
1012
1013 r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
1014 if (r < 0) {
1015 pam_syslog(handle, LOG_ERR, "Failed to store new password: %s", strerror_safe(r));
1016 return PAM_SERVICE_ERR;
1017 }
1018
1019 for (;;) {
1020 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1021 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1022
8a1596aa 1023 r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
26cf9fb7
LP
1024 if (r < 0)
1025 return pam_bus_log_create_error(handle, r);
1026
1027 r = sd_bus_message_append(m, "s", ur->user_name);
1028 if (r < 0)
1029 return pam_bus_log_create_error(handle, r);
1030
1031 r = bus_message_append_secret(m, new_secret);
1032 if (r < 0)
1033 return pam_bus_log_create_error(handle, r);
1034
1035 r = bus_message_append_secret(m, old_secret);
1036 if (r < 0)
1037 return pam_bus_log_create_error(handle, r);
1038
1039 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
1040 if (r < 0) {
1041 r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
1042 if (r == PAM_CONV_ERR) {
1043 pam_syslog(handle, LOG_ERR, "Failed to prompt for password/prompt.");
1044 return PAM_CONV_ERR;
1045 }
1046 if (r != PAM_SUCCESS)
1047 return r;
1048 } else {
1049 pam_syslog(handle, LOG_NOTICE, "Successfully changed password for user %s.", ur->user_name);
1050 return PAM_SUCCESS;
1051 }
1052
1053 if (++n_attempts >= 5)
1054 break;
1055
1056 /* Try again */
1057 };
1058
1059 pam_syslog(handle, LOG_NOTICE, "Failed to change password for user %s: %m", ur->user_name);
1060 return PAM_MAXTRIES;
1061}