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