]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pam-util: implement our own pam_prompt() replacement, that doesn't log loudly
authorLennart Poettering <lennart@poettering.net>
Tue, 28 Nov 2023 10:02:56 +0000 (11:02 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 10 Jan 2024 19:06:40 +0000 (19:06 +0000)
pam_prompt() will log very noisely at high error levels if it is called
without a conversation function that works. This is however a frequent
case, given that ssh doesn't provide one. To tone down the misleading
logging a bit, implement our own pam_prompt_graceful() that is just like
pam_prompt(), but reports errors back the caller who then logs (which we
generally do anyway).

src/home/pam_systemd_home.c
src/shared/pam-util.c
src/shared/pam-util.h

index 9a6b3b4487cc242f1f2e63f62a5d8e1da3235d50..7460aebc567ef770856535768cf3809b8b15cbe7 100644 (file)
@@ -288,13 +288,13 @@ static int handle_generic_user_record_error(
         /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
 
         if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL,
+                (void) pam_prompt_graceful(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);
                 return pam_syslog_pam_error(handle, LOG_ERR, PAM_PERM_DENIED,
                                             "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Too frequent login attempts for user %s, try again later."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too frequent login attempts for user %s, try again later."), user_name);
                 return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
                                             "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
 
@@ -306,10 +306,10 @@ static int handle_generic_user_record_error(
                 /* This didn't work? Ask for an (additional?) password */
 
                 if (strv_isempty(secret->password))
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Password: "));
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Password: "));
                 else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient for authentication of user %s."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, try again: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient for authentication of user %s."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, try again: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -331,10 +331,10 @@ static int handle_generic_user_record_error(
                 /* Hmm, homed asks for recovery key (because no regular password is defined maybe)? Provide it. */
 
                 if (strv_isempty(secret->password))
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Recovery key: "));
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Recovery key: "));
                 else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password/recovery key incorrect or not sufficient for authentication of user %s."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, reenter recovery key: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password/recovery key incorrect or not sufficient for authentication of user %s."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, reenter recovery key: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -354,11 +354,11 @@ static int handle_generic_user_record_error(
                 assert(secret);
 
                 if (strv_isempty(secret->password)) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token of user %s not inserted."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token of user %s not inserted."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
                 } else {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient, and configured security token of user %s not inserted."), user_name);
-                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password incorrect or not sufficient, and configured security token of user %s not inserted."), user_name);
+                        r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Try again with password: "));
                 }
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
@@ -377,7 +377,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Security token PIN: "));
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -394,7 +394,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please authenticate physically on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please authenticate physically on security token of user %s."), user_name);
 
                 r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
                 if (r < 0)
@@ -405,7 +405,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please confirm presence on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please confirm presence on security token of user %s."), user_name);
 
                 r = user_record_set_fido2_user_presence_permitted(secret, true);
                 if (r < 0)
@@ -416,7 +416,7 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Please verify user on security token of user %s."), user_name);
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Please verify user on security token of user %s."), user_name);
 
                 r = user_record_set_fido2_user_verification_permitted(secret, true);
                 if (r < 0)
@@ -425,7 +425,7 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"));
                 return PAM_SERVICE_ERR;
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
@@ -433,8 +433,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN incorrect for user %s."), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN incorrect for user %s."), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -452,8 +452,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only a few tries left!)"), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only a few tries left!)"), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -471,8 +471,8 @@ static int handle_generic_user_record_error(
 
                 assert(secret);
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only one try left!)"), user_name);
-                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Security token PIN of user %s incorrect (only one try left!)"), user_name);
+                r = pam_prompt_graceful(handle, PAM_PROMPT_ECHO_OFF, &newp, _("Sorry, retry security token PIN: "));
                 if (r != PAM_SUCCESS)
                         return PAM_CONV_ERR; /* no logging here */
 
@@ -616,9 +616,9 @@ static int acquire_home(
                                          * failure. */
 
                                         if (home_not_active)
-                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently not active, please log in locally first."), ur->user_name);
+                                                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently not active, please log in locally first."), ur->user_name);
                                         if (home_locked)
-                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name);
+                                                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Home of user %s is currently locked, please unlock locally first."), ur->user_name);
 
                                         if (FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) || debug)
                                                 pam_syslog(handle, FLAGS_SET(flags, ACQUIRE_MUST_AUTHENTICATE) ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
@@ -643,7 +643,7 @@ static int acquire_home(
                 }
 
                 if (++n_attempts >= 5) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL,
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL,
                                           _("Too many unsuccessful login attempts for user %s, refusing."), ur->user_name);
                         return pam_syslog_pam_error(handle, LOG_ERR, PAM_MAXTRIES,
                                                     "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
@@ -870,20 +870,20 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
                 break;
 
         case -ENOLCK:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is blocked, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         case -EL2HLT:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid yet, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         case -EL3HLT:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record is not valid anymore, prohibiting access."));
                 return PAM_ACCT_EXPIRED;
 
         default:
                 if (r < 0) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
                         return PAM_ACCT_EXPIRED;
                 }
 
@@ -895,7 +895,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
                 usec_t n = now(CLOCK_REALTIME);
 
                 if (t > n) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Too many logins, try again in %s."),
                                           FORMAT_TIMESPAN(t - n, USEC_PER_SEC));
 
                         return PAM_MAXTRIES;
@@ -906,21 +906,21 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         switch (r) {
 
         case -EKEYREVOKED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password change required."));
                 return PAM_NEW_AUTHTOK_REQD;
 
         case -EOWNERDEAD:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password expired, change required."));
                 return PAM_NEW_AUTHTOK_REQD;
 
         /* Strictly speaking this is only about password expiration, and we might want to allow
          * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
         case -EKEYREJECTED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password is expired, but can't change, refusing login."));
                 return PAM_AUTHTOK_EXPIRED;
 
         case -EKEYEXPIRED:
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
+                (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("Password will expire soon, please change."));
                 break;
 
         case -ESTALE:
@@ -934,7 +934,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
 
         default:
                 if (r < 0) {
-                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
+                        (void) pam_prompt_graceful(handle, PAM_ERROR_MSG, NULL, _("User record not valid, prohibiting access."));
                         return PAM_AUTHTOK_EXPIRED;
                 }
 
index 59437ae0bb9fb6c58a4034fcdd9aecf5806f4728..13b68234c9bf5b838b66d5a5d3758b2395a7968d 100644 (file)
@@ -226,3 +226,45 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
 
         return r;
 }
+
+int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
+        va_list args;
+        int r;
+
+        assert(handle);
+        assert(fmt);
+
+        /* This is just like pam_prompt(), but does not noisily (i.e. beyond LOG_DEBUG) log on its own, but leaves that to the caller */
+
+        _cleanup_free_ char *msg = NULL;
+        va_start(args, fmt);
+        r = vasprintf(&msg, fmt, args);
+        va_end(args);
+        if (r < 0)
+                return PAM_BUF_ERR;
+
+        const struct pam_conv *conv = NULL;
+        r = pam_get_item(handle, PAM_CONV, (const void**) &conv);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_BAD_ITEM))
+                return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Failed to get conversation function structure: @PAMERR@");
+        if (!conv || !conv->conv) {
+                pam_syslog(handle, LOG_DEBUG, "No conversation function.");
+                return PAM_SYSTEM_ERR;
+        }
+
+        struct pam_message message = {
+                .msg_style = style,
+                .msg = msg,
+        };
+        const struct pam_message *pmessage = &message;
+        _cleanup_free_ struct pam_response *response = NULL;
+        r = conv->conv(1, &pmessage, &response, conv->appdata_ptr);
+        _cleanup_(erase_and_freep) char *rr = response ? response->resp : NULL; /* make sure string is freed + erased */
+        if (r != PAM_SUCCESS)
+                return pam_syslog_pam_error(handle, LOG_DEBUG, r, "Conversation function failed: @PAMERR@");
+
+        if (ret_response)
+                *ret_response = TAKE_PTR(rr);
+
+        return PAM_SUCCESS;
+}
index 9c40ba2dded7189f685e897381954ba548d59dfe..3439d4246e1636edb6064ac281e0c03869e8f43e 100644 (file)
@@ -43,3 +43,5 @@ void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
 int pam_get_item_many_internal(pam_handle_t *handle, ...);
 
 #define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
+
+int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);