From: Lennart Poettering Date: Tue, 28 Nov 2023 10:02:56 +0000 (+0100) Subject: pam-util: implement our own pam_prompt() replacement, that doesn't log loudly X-Git-Tag: v256-rc1~1208 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=52e5b950dcc0cf047d61f5813695ec8f28b28e3d;p=thirdparty%2Fsystemd.git pam-util: implement our own pam_prompt() replacement, that doesn't log loudly 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). --- diff --git a/src/home/pam_systemd_home.c b/src/home/pam_systemd_home.c index 9a6b3b4487c..7460aebc567 100644 --- a/src/home/pam_systemd_home.c +++ b/src/home/pam_systemd_home.c @@ -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; } diff --git a/src/shared/pam-util.c b/src/shared/pam-util.c index 59437ae0bb9..13b68234c9b 100644 --- a/src/shared/pam-util.c +++ b/src/shared/pam-util.c @@ -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; +} diff --git a/src/shared/pam-util.h b/src/shared/pam-util.h index 9c40ba2dded..3439d4246e1 100644 --- a/src/shared/pam-util.h +++ b/src/shared/pam-util.h @@ -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);