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