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