]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/pam_systemd_home.c
sd-network: DHCPv6 - Add status codes
[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"
ea7cbf5b 9#include "bus-util.h"
26cf9fb7
LP
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/* Used for the "systemd-user-record-is-homed" PAM data field, to indicate whether we know whether this user
22 * record is managed by homed or by something else. */
23#define USER_RECORD_IS_HOMED INT_TO_PTR(1)
24#define USER_RECORD_IS_OTHER INT_TO_PTR(2)
25
ea7cbf5b
VC
26static const BusLocator home_mgr = {
27 .destination = "org.freedesktop.home1",
28 .path = "/org/freedesktop/home1",
29 .interface = "org.freedesktop.home1.Manager",
30};
31
26cf9fb7
LP
32static int parse_argv(
33 pam_handle_t *handle,
34 int argc, const char **argv,
35 bool *please_suspend,
36 bool *debug) {
37
38 int i;
39
40 assert(argc >= 0);
41 assert(argc == 0 || argv);
42
43 for (i = 0; i < argc; i++) {
44 const char *v;
45
f12d19b3 46 if ((v = startswith(argv[i], "suspend="))) {
26cf9fb7
LP
47 int k;
48
49 k = parse_boolean(v);
50 if (k < 0)
80ace4f2 51 pam_syslog(handle, LOG_WARNING, "Failed to parse suspend= argument, ignoring: %s", v);
26cf9fb7
LP
52 else if (please_suspend)
53 *please_suspend = k;
54
55 } else if ((v = startswith(argv[i], "debug="))) {
56 int k;
57
58 k = parse_boolean(v);
59 if (k < 0)
60 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
61 else if (debug)
62 *debug = k;
63
64 } else
65 pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
66 }
67
68 return 0;
69}
70
71static int acquire_user_record(
72 pam_handle_t *handle,
73 UserRecord **ret_record) {
74
75 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
76 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
77 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
78 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
79 const char *username = NULL, *json = NULL;
80 const void *b = NULL;
81 int r;
82
83 assert(handle);
84
85 r = pam_get_user(handle, &username, NULL);
86 if (r != PAM_SUCCESS) {
87 pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
88 return r;
89 }
90
91 if (isempty(username)) {
92 pam_syslog(handle, LOG_ERR, "User name not set.");
93 return PAM_SERVICE_ERR;
94 }
95
96 /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
97 * user names we don't consider valid. */
7a8867ab 98 if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
26cf9fb7
LP
99 return PAM_USER_UNKNOWN;
100
101 /* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */
102 r = pam_get_data(handle, "systemd-user-record-is-homed", &b);
103 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
104 /* Failure */
80ace4f2 105 pam_syslog(handle, LOG_ERR, "Failed to get PAM user-record-is-homed flag: %s", pam_strerror(handle, r));
26cf9fb7
LP
106 return r;
107 } else if (b == NULL)
108 /* Nothing cached yet, need to acquire fresh */
109 json = NULL;
110 else if (b != USER_RECORD_IS_HOMED)
111 /* Definitely not a homed record */
112 return PAM_USER_UNKNOWN;
113 else {
114 /* It's a homed record, let's use the cache, so that we can share it between the session and
115 * the authentication hooks */
116 r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
117 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
118 pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
119 return r;
120 }
121 }
122
123 if (!json) {
124 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
125 _cleanup_free_ char *json_copy = NULL;
126
127 r = pam_acquire_bus_connection(handle, &bus);
128 if (r != PAM_SUCCESS)
129 return r;
130
ea7cbf5b 131 r = bus_call_method(bus, &home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
26cf9fb7
LP
132 if (r < 0) {
133 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
134 sd_bus_error_has_name(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
135 pam_syslog(handle, LOG_DEBUG, "systemd-homed is not available: %s", bus_error_message(&error, r));
136 goto user_unknown;
137 }
138
139 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_HOME)) {
140 pam_syslog(handle, LOG_DEBUG, "Not a user managed by systemd-homed: %s", bus_error_message(&error, r));
141 goto user_unknown;
142 }
143
144 pam_syslog(handle, LOG_ERR, "Failed to query user record: %s", bus_error_message(&error, r));
145 return PAM_SERVICE_ERR;
146 }
147
148 r = sd_bus_message_read(reply, "sbo", &json, NULL, NULL);
149 if (r < 0)
150 return pam_bus_log_parse_error(handle, r);
151
152 json_copy = strdup(json);
153 if (!json_copy)
154 return pam_log_oom(handle);
155
156 r = pam_set_data(handle, "systemd-user-record", json_copy, pam_cleanup_free);
157 if (r != PAM_SUCCESS) {
158 pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
159 return r;
160 }
161
162 TAKE_PTR(json_copy);
163
164 r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL);
165 if (r != PAM_SUCCESS) {
166 pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r));
167 return r;
168 }
169 }
170
171 r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
172 if (r < 0) {
173 pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
174 return PAM_SERVICE_ERR;
175 }
176
177 ur = user_record_new();
178 if (!ur)
179 return pam_log_oom(handle);
180
181 r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET);
182 if (r < 0) {
183 pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
184 return PAM_SERVICE_ERR;
185 }
186
187 if (!streq_ptr(username, ur->user_name)) {
188 pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
189 return PAM_SERVICE_ERR;
190 }
191
192 if (ret_record)
193 *ret_record = TAKE_PTR(ur);
194
195 return PAM_SUCCESS;
196
197user_unknown:
198 /* Cache this, so that we don't check again */
199 r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_OTHER, NULL);
200 if (r != PAM_SUCCESS)
80ace4f2 201 pam_syslog(handle, LOG_ERR, "Failed to set PAM user-record-is-homed flag, ignoring: %s", pam_strerror(handle, r));
26cf9fb7
LP
202
203 return PAM_USER_UNKNOWN;
204}
205
206static int release_user_record(pam_handle_t *handle) {
207 int r, k;
208
209 r = pam_set_data(handle, "systemd-user-record", NULL, NULL);
210 if (r != PAM_SUCCESS)
211 pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
212
213 k = pam_set_data(handle, "systemd-user-record-is-homed", NULL, NULL);
214 if (k != PAM_SUCCESS)
80ace4f2 215 pam_syslog(handle, LOG_ERR, "Failed to release PAM user-record-is-homed flag: %s", pam_strerror(handle, k));
26cf9fb7
LP
216
217 return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
218}
219
220static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
221 safe_close(PTR_TO_FD(data));
222}
223
224static int handle_generic_user_record_error(
225 pam_handle_t *handle,
226 const char *user_name,
227 UserRecord *secret,
228 int ret,
229 const sd_bus_error *error) {
230
231 assert(user_name);
232 assert(secret);
233 assert(error);
234
235 int r;
236
237 /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
238
239 if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
240 (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);
241 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
242 return PAM_PERM_DENIED;
243
244 } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
245 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too frequent unsuccessful login attempts for user %s, try again later.", user_name);
246 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
247 return PAM_MAXTRIES;
248
249 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
250 _cleanup_(erase_and_freep) char *newp = NULL;
251
252 /* This didn't work? Ask for an (additional?) password */
253
254 if (strv_isempty(secret->password))
255 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password: ");
256 else
257 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient for authentication of user %s, please try again: ", user_name);
258 if (r != PAM_SUCCESS)
259 return PAM_CONV_ERR; /* no logging here */
260
261 if (isempty(newp)) {
262 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
263 return PAM_AUTHTOK_ERR;
264 }
265
266 r = user_record_set_password(secret, STRV_MAKE(newp), true);
267 if (r < 0) {
268 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
269 return PAM_SERVICE_ERR;
270 }
271
272 } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
273 _cleanup_(erase_and_freep) char *newp = NULL;
274
275 if (strv_isempty(secret->password))
276 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token of user %s not inserted, please enter password: ", user_name);
277 else
278 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient, and configured security token of user %s not inserted, please enter password: ", user_name);
279 if (r != PAM_SUCCESS)
280 return PAM_CONV_ERR; /* no logging here */
281
282 if (isempty(newp)) {
283 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
284 return PAM_AUTHTOK_ERR;
285 }
286
287 r = user_record_set_password(secret, STRV_MAKE(newp), true);
288 if (r < 0) {
289 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
290 return PAM_SERVICE_ERR;
291 }
292
293 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
294 _cleanup_(erase_and_freep) char *newp = NULL;
295
296 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Please enter security token PIN: ");
297 if (r != PAM_SUCCESS)
298 return PAM_CONV_ERR; /* no logging here */
299
300 if (isempty(newp)) {
301 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
302 return PAM_AUTHTOK_ERR;
303 }
304
305 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
306 if (r < 0) {
307 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
308 return PAM_SERVICE_ERR;
309 }
310
311 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
312
313 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please authenticate physically on security token of user %s.", user_name);
314
315 r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
316 if (r < 0) {
317 pam_syslog(handle, LOG_ERR, "Failed to set PKCS#11 protected authentication path permitted flag: %s", strerror_safe(r));
318 return PAM_SERVICE_ERR;
319 }
320
321 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
322 _cleanup_(erase_and_freep) char *newp = NULL;
323
324 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect, please enter PIN for security token of user %s again: ", user_name);
325 if (r != PAM_SUCCESS)
326 return PAM_CONV_ERR; /* no logging here */
327
328 if (isempty(newp)) {
329 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
330 return PAM_AUTHTOK_ERR;
331 }
332
333 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
334 if (r < 0) {
335 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
336 return PAM_SERVICE_ERR;
337 }
338
339 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
340 _cleanup_(erase_and_freep) char *newp = NULL;
341
342 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only a few tries left!), please enter PIN for security token of user %s again: ", user_name);
343 if (r != PAM_SUCCESS)
344 return PAM_CONV_ERR; /* no logging here */
345
346 if (isempty(newp)) {
347 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
348 return PAM_AUTHTOK_ERR;
349 }
350
351 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
352 if (r < 0) {
353 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
354 return PAM_SERVICE_ERR;
355 }
356
357 } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
358 _cleanup_(erase_and_freep) char *newp = NULL;
359
360 r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only one try left!), please enter PIN for security token of user %s again: ", user_name);
361 if (r != PAM_SUCCESS)
362 return PAM_CONV_ERR; /* no logging here */
363
364 if (isempty(newp)) {
365 pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
366 return PAM_AUTHTOK_ERR;
367 }
368
369 r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
370 if (r < 0) {
371 pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
372 return PAM_SERVICE_ERR;
373 }
374
375 } else {
376 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
377 return PAM_SERVICE_ERR;
378 }
379
380 return PAM_SUCCESS;
381}
382
383static int acquire_home(
384 pam_handle_t *handle,
385 bool please_authenticate,
386 bool please_suspend,
387 bool debug) {
388
389 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
390 bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
391 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
392 _cleanup_close_ int acquired_fd = -1;
393 const void *home_fd_ptr = NULL;
394 unsigned n_attempts = 0;
395 int r;
396
397 assert(handle);
398
399 /* This acquires a reference to a home directory in one of two ways: if please_authenticate is true,
400 * then we'll call AcquireHome() after asking the user for a password. Otherwise it tries to call
401 * RefHome() and if that fails queries the user for a password and uses AcquireHome().
402 *
403 * The idea is that the PAM authentication hook sets please_authenticate and thus always
404 * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
405 * authentication if possible, but with authentication if necessary. */
406
407 /* If we already have acquired the fd, let's shortcut this */
408 r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
409 if (r == PAM_SUCCESS && PTR_TO_INT(home_fd_ptr) >= 0)
410 return PAM_SUCCESS;
411
412 r = pam_acquire_bus_connection(handle, &bus);
413 if (r != PAM_SUCCESS)
414 return r;
415
416 r = acquire_user_record(handle, &ur);
417 if (r != PAM_SUCCESS)
418 return r;
419
420 /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
421 * might happen that the the record we stored on the host does not match the encryption password of
422 * the LUKS image in case the image was used in a different system where the password was
423 * changed. In that case it will happen that the LUKS password and the host password are
424 * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
425 * treat bad passwords as a request to collect one more password and pass the new all all previously
426 * used passwords again. */
427
428 for (;;) {
429 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
430 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
431
432 if (do_auth && !secret) {
433 const char *cached_password = NULL;
434
435 secret = user_record_new();
436 if (!secret)
437 return pam_log_oom(handle);
438
439 /* If there's already a cached password, use it. But if not let's authenticate
440 * without anything, maybe some other authentication mechanism systemd-homed
441 * implements (such as PKCS#11) allows us to authenticate without anything else. */
442 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &cached_password);
443 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
444 pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
445 return r;
446 }
447
448 if (!isempty(cached_password)) {
449 r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
450 if (r < 0) {
451 pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
452 return PAM_SERVICE_ERR;
453 }
454 }
455 }
456
ea7cbf5b 457 r = bus_message_new_method_call(bus, &m, &home_mgr, do_auth ? "AcquireHome" : "RefHome");
26cf9fb7
LP
458 if (r < 0)
459 return pam_bus_log_create_error(handle, r);
460
461 r = sd_bus_message_append(m, "s", ur->user_name);
462 if (r < 0)
463 return pam_bus_log_create_error(handle, r);
464
465 if (do_auth) {
466 r = bus_message_append_secret(m, secret);
467 if (r < 0)
468 return pam_bus_log_create_error(handle, r);
469 }
470
471 r = sd_bus_message_append(m, "b", please_suspend);
472 if (r < 0)
473 return pam_bus_log_create_error(handle, r);
474
475 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
476 if (r < 0) {
477
478 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_NOT_ACTIVE))
479 /* Only on RefHome(): We can't access the home directory currently, unless
480 * it's unlocked with a password. Hence, let's try this again, this time with
481 * authentication. */
482 home_not_active = true;
483 else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
484 home_locked = true; /* Similar */
485 else {
486 r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error);
487 if (r == PAM_CONV_ERR) {
488 /* Password/PIN prompts will fail in certain environments, for example when
489 * we are called from OpenSSH's account or session hooks, or in systemd's
490 * per-service PAM logic. In that case, print a friendly message and accept
491 * failure. */
492
493 if (home_not_active)
494 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently not active, please log in locally first.", ur->user_name);
495 if (home_locked)
496 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
497
498 pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
499
500 return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
501 }
502 if (r != PAM_SUCCESS)
503 return r;
504 }
505
506 } else {
507 int fd;
508
509 r = sd_bus_message_read(reply, "h", &fd);
510 if (r < 0)
511 return pam_bus_log_parse_error(handle, r);
512
513 acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
514 if (acquired_fd < 0) {
515 pam_syslog(handle, LOG_ERR, "Failed to duplicate acquired fd: %s", bus_error_message(&error, r));
516 return PAM_SERVICE_ERR;
517 }
518
519 break;
520 }
521
522 if (++n_attempts >= 5) {
523 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many unsuccessful login attempts for user %s, refusing.", ur->user_name);
524 pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
525 return PAM_MAXTRIES;
526 }
527
528 /* Try again, this time with authentication if we didn't do that before. */
529 do_auth = true;
530 }
531
532 r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd);
533 if (r < 0) {
534 pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
535 return r;
536 }
537 TAKE_FD(acquired_fd);
538
539 if (do_auth) {
540 /* We likely just activated the home directory, let's flush out the user record, since a
541 * newer embedded user record might have been acquired from the activation. */
542
543 r = release_user_record(handle);
544 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
545 return r;
546 }
547
548 pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
549
550 return PAM_SUCCESS;
551}
552
553static int release_home_fd(pam_handle_t *handle) {
554 const void *home_fd_ptr = NULL;
555 int r;
556
557 r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
558 if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0)
559 return PAM_NO_MODULE_DATA;
560
561 r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
562 if (r != PAM_SUCCESS)
563 pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
564
565 return r;
566}
567
568_public_ PAM_EXTERN int pam_sm_authenticate(
569 pam_handle_t *handle,
570 int flags,
571 int argc, const char **argv) {
572
573 bool debug = false, suspend_please = false;
574
575 if (parse_argv(handle,
576 argc, argv,
577 &suspend_please,
578 &debug) < 0)
579 return PAM_AUTH_ERR;
580
581 if (debug)
582 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed authenticating");
583
584 return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug);
585}
586
587_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
588 return PAM_SUCCESS;
589}
590
591_public_ PAM_EXTERN int pam_sm_open_session(
592 pam_handle_t *handle,
593 int flags,
594 int argc, const char **argv) {
595
596 bool debug = false, suspend_please = false;
597 int r;
598
599 if (parse_argv(handle,
600 argc, argv,
601 &suspend_please,
602 &debug) < 0)
603 return PAM_SESSION_ERR;
604
605 if (debug)
606 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session start");
607
608 r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug);
609 if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
610 return PAM_SUCCESS;
611 if (r != PAM_SUCCESS)
612 return r;
613
614 r = pam_putenv(handle, "SYSTEMD_HOME=1");
615 if (r != PAM_SUCCESS) {
616 pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME: %s", pam_strerror(handle, r));
617 return r;
618 }
619
620 /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
621 * not going to process the bus connection in that time, so let's better close before the daemon
622 * kicks us off because we are not processing anything. */
623 (void) pam_release_bus_connection(handle);
624 return PAM_SUCCESS;
625}
626
627_public_ PAM_EXTERN int pam_sm_close_session(
628 pam_handle_t *handle,
629 int flags,
630 int argc, const char **argv) {
631
632 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
633 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
634 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
635 const char *username = NULL;
636 bool debug = false;
637 int r;
638
639 if (parse_argv(handle,
640 argc, argv,
641 NULL,
642 &debug) < 0)
643 return PAM_SESSION_ERR;
644
645 if (debug)
646 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
647
648 /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
649 * call will be able to do its thing. */
650 r = release_home_fd(handle);
651 if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
652 return PAM_SUCCESS;
653 if (r != PAM_SUCCESS)
654 return r;
655
656 r = pam_get_user(handle, &username, NULL);
657 if (r != PAM_SUCCESS) {
658 pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
659 return r;
660 }
661
662 r = pam_acquire_bus_connection(handle, &bus);
663 if (r != PAM_SUCCESS)
664 return r;
665
ea7cbf5b 666 r = bus_message_new_method_call(bus, &m, &home_mgr, "ReleaseHome");
26cf9fb7
LP
667 if (r < 0)
668 return pam_bus_log_create_error(handle, r);
669
670 r = sd_bus_message_append(m, "s", username);
671 if (r < 0)
672 return pam_bus_log_create_error(handle, r);
673
674 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
675 if (r < 0) {
676 if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
677 pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
678 else {
679 pam_syslog(handle, LOG_ERR, "Failed to release user home: %s", bus_error_message(&error, r));
680 return PAM_SESSION_ERR;
681 }
682 }
683
684 return PAM_SUCCESS;
685}
686
687_public_ PAM_EXTERN int pam_sm_acct_mgmt(
688 pam_handle_t *handle,
689 int flags,
690 int argc,
691 const char **argv) {
692
693 _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
694 bool debug = false, please_suspend = false;
695 usec_t t;
696 int r;
697
698 if (parse_argv(handle,
699 argc, argv,
700 &please_suspend,
701 &debug) < 0)
702 return PAM_AUTH_ERR;
703
704 if (debug)
705 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
706
707 r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug);
708 if (r == PAM_USER_UNKNOWN)
709 return PAM_SUCCESS; /* we don't have anything to say about users we don't manage */
710 if (r != PAM_SUCCESS)
711 return r;
712
713 r = acquire_user_record(handle, &ur);
714 if (r != PAM_SUCCESS)
715 return r;
716
717 r = user_record_test_blocked(ur);
718 switch (r) {
719
720 case -ESTALE:
721 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access.");
722 return PAM_ACCT_EXPIRED;
723
724 case -ENOLCK:
725 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
726 return PAM_ACCT_EXPIRED;
727
728 case -EL2HLT:
729 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid yet, prohibiting access.");
730 return PAM_ACCT_EXPIRED;
731
732 case -EL3HLT:
733 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid anymore, prohibiting access.");
734 return PAM_ACCT_EXPIRED;
735
736 default:
737 if (r < 0) {
738 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
739 return PAM_ACCT_EXPIRED;
740 }
741
742 break;
743 }
744
745 t = user_record_ratelimit_next_try(ur);
746 if (t != USEC_INFINITY) {
747 usec_t n = now(CLOCK_REALTIME);
748
749 if (t > n) {
750 char buf[FORMAT_TIMESPAN_MAX];
751 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many logins, try again in %s.",
752 format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
753
754 return PAM_MAXTRIES;
755 }
756 }
757
758 r = user_record_test_password_change_required(ur);
759 switch (r) {
760
761 case -EKEYREVOKED:
762 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password change required.");
763 return PAM_NEW_AUTHTOK_REQD;
764
765 case -EOWNERDEAD:
766 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password expired, change requird.");
767 return PAM_NEW_AUTHTOK_REQD;
768
769 case -EKEYREJECTED:
770 /* Strictly speaking this is only about password expiration, and we might want to allow
771 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
772 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password is expired, but can't change, refusing login.");
773 return PAM_AUTHTOK_EXPIRED;
774
775 case -EKEYEXPIRED:
776 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
777 break;
778
779 case -EROFS:
780 /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
781 break;
782
783 default:
784 if (r < 0) {
785 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
786 return PAM_AUTHTOK_EXPIRED;
787 }
788
789 break;
790 }
791
792 return PAM_SUCCESS;
793}
794
795_public_ PAM_EXTERN int pam_sm_chauthtok(
796 pam_handle_t *handle,
797 int flags,
798 int argc,
799 const char **argv) {
800
801 _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
802 const char *old_password = NULL, *new_password = NULL;
803 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
804 unsigned n_attempts = 0;
805 bool debug = false;
806 int r;
807
808 if (parse_argv(handle,
809 argc, argv,
810 NULL,
811 &debug) < 0)
812 return PAM_AUTH_ERR;
813
814 if (debug)
815 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
816
817 r = pam_acquire_bus_connection(handle, &bus);
818 if (r != PAM_SUCCESS)
819 return r;
820
821 r = acquire_user_record(handle, &ur);
822 if (r != PAM_SUCCESS)
823 return r;
824
825 /* Start with cached credentials */
826 r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
827 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
828 pam_syslog(handle, LOG_ERR, "Failed to get old password: %s", pam_strerror(handle, r));
829 return r;
830 }
831 r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
832 if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
833 pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
834 return r;
835 }
836
837 if (isempty(new_password)) {
838 /* No, it's not cached, then let's ask for the password and its verification, and cache
839 * it. */
840
841 r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
842 if (r != PAM_SUCCESS) {
843 pam_syslog(handle, LOG_ERR, "Failed to get new password: %s", pam_strerror(handle, r));
844 return r;
845 }
846 if (isempty(new_password)) {
847 pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
848 return PAM_AUTHTOK_ERR;
849 }
850
851 r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
852 if (r != PAM_SUCCESS) {
853 pam_syslog(handle, LOG_ERR, "Failed to get password again: %s", pam_strerror(handle, r));
854 return r;
855 }
856
857 // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
858 // that, and instead assume the password was already verified once when it is found to be
859 // cached already. needs to be fixed in pam_pwquality
860 }
861
862 /* Now everything is cached and checked, let's exit from the preliminary check */
863 if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
864 return PAM_SUCCESS;
865
26cf9fb7
LP
866 old_secret = user_record_new();
867 if (!old_secret)
868 return pam_log_oom(handle);
869
870 if (!isempty(old_password)) {
871 r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
872 if (r < 0) {
873 pam_syslog(handle, LOG_ERR, "Failed to store old password: %s", strerror_safe(r));
874 return PAM_SERVICE_ERR;
875 }
876 }
877
878 new_secret = user_record_new();
879 if (!new_secret)
880 return pam_log_oom(handle);
881
882 r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
883 if (r < 0) {
884 pam_syslog(handle, LOG_ERR, "Failed to store new password: %s", strerror_safe(r));
885 return PAM_SERVICE_ERR;
886 }
887
888 for (;;) {
889 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
890 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
891
ea7cbf5b 892 r = bus_message_new_method_call(bus, &m, &home_mgr, "ChangePasswordHome");
26cf9fb7
LP
893 if (r < 0)
894 return pam_bus_log_create_error(handle, r);
895
896 r = sd_bus_message_append(m, "s", ur->user_name);
897 if (r < 0)
898 return pam_bus_log_create_error(handle, r);
899
900 r = bus_message_append_secret(m, new_secret);
901 if (r < 0)
902 return pam_bus_log_create_error(handle, r);
903
904 r = bus_message_append_secret(m, old_secret);
905 if (r < 0)
906 return pam_bus_log_create_error(handle, r);
907
908 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
909 if (r < 0) {
910 r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
911 if (r == PAM_CONV_ERR) {
912 pam_syslog(handle, LOG_ERR, "Failed to prompt for password/prompt.");
913 return PAM_CONV_ERR;
914 }
915 if (r != PAM_SUCCESS)
916 return r;
917 } else {
918 pam_syslog(handle, LOG_NOTICE, "Successfully changed password for user %s.", ur->user_name);
919 return PAM_SUCCESS;
920 }
921
922 if (++n_attempts >= 5)
923 break;
924
925 /* Try again */
926 };
927
928 pam_syslog(handle, LOG_NOTICE, "Failed to change password for user %s: %m", ur->user_name);
929 return PAM_MAXTRIES;
930}