| #ifndef THIRD_PARTY_GBMCWEB_INCLUDE_PAM_AUTHENTICATE_H_ | |
| #define THIRD_PARTY_GBMCWEB_INCLUDE_PAM_AUTHENTICATE_H_ | |
| #include <security/_pam_types.h> | |
| #include <security/pam_appl.h> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <string> | |
| #include <string_view> | |
| #include "boost/utility/string_view.hpp" // NOLINT | |
| // function used to get user input | |
| inline int pamFunctionConversation(int numMsg, const struct pam_message** msg, | |
| struct pam_response** resp, | |
| void* appdataPtr) { | |
| if (appdataPtr == nullptr) { | |
| return PAM_CONV_ERR; | |
| } | |
| if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG) { | |
| return PAM_CONV_ERR; | |
| } | |
| for (int i = 0; i < numMsg; ++i) { | |
| /* Ignore all PAM messages except prompting for hidden input */ | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) | |
| if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) { | |
| continue; | |
| } | |
| /* Assume PAM is only prompting for the password as hidden input */ | |
| /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encountered */ | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) | |
| char* app_pass = reinterpret_cast<char*>(appdataPtr); | |
| size_t app_pass_size = std::strlen(app_pass); | |
| if ((app_pass_size + 1) > PAM_MAX_RESP_SIZE) { | |
| return PAM_CONV_ERR; | |
| } | |
| // IDeally we'd like to avoid using malloc here, but because we're | |
| // passing off ownership of this to a C application, there aren't a lot | |
| // of sane ways to avoid it. | |
| // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)' | |
| void* pass_ptr = malloc(app_pass_size + 1); | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) | |
| char* pass = reinterpret_cast<char*>(pass_ptr); | |
| if (pass == nullptr) { | |
| return PAM_BUF_ERR; | |
| } | |
| std::strncpy(pass, app_pass, app_pass_size + 1); | |
| size_t num_msg_size = static_cast<size_t>(numMsg); | |
| // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) | |
| void* ptr = calloc(num_msg_size, sizeof(struct pam_response)); | |
| if (ptr == nullptr) { | |
| // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) | |
| free(pass); | |
| return PAM_BUF_ERR; | |
| } | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) | |
| *resp = reinterpret_cast<pam_response*>(ptr); | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) | |
| resp[i]->resp = pass; | |
| return PAM_SUCCESS; | |
| } | |
| return PAM_CONV_ERR; | |
| } | |
| /** | |
| * @brief Attempt username/password authentication via PAM. | |
| * @param username The provided username aka account name. | |
| * @param password The provided password. | |
| * @returns PAM error code or PAM_SUCCESS for success. */ | |
| inline int pamAuthenticateUser(std::string_view username, | |
| std::string_view password) { | |
| std::string user_str(username); | |
| std::string pass_str(password); | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) | |
| char* pass_str_no_const = const_cast<char*>(pass_str.c_str()); | |
| const struct pam_conv local_conversation = {pamFunctionConversation, | |
| pass_str_no_const}; | |
| pam_handle_t* local_auth_handle = nullptr; // this gets set by pam_start | |
| int retval = pam_start("webserver", user_str.c_str(), &local_conversation, | |
| &local_auth_handle); | |
| if (retval != PAM_SUCCESS) { | |
| return retval; | |
| } | |
| retval = pam_authenticate(local_auth_handle, | |
| PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); | |
| if (retval != PAM_SUCCESS) { | |
| pam_end(local_auth_handle, PAM_SUCCESS); // ignore retval | |
| return retval; | |
| } | |
| /* check that the account is healthy */ | |
| retval = pam_acct_mgmt(local_auth_handle, PAM_DISALLOW_NULL_AUTHTOK); | |
| if (retval != PAM_SUCCESS) { | |
| pam_end(local_auth_handle, PAM_SUCCESS); // ignore retval | |
| return retval; | |
| } | |
| return pam_end(local_auth_handle, PAM_SUCCESS); | |
| } | |
| inline int pamUpdatePassword(const std::string& username, | |
| const std::string& password) { | |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) | |
| char* pass_str_no_const = const_cast<char*>(password.c_str()); | |
| const struct pam_conv local_conversation = {pamFunctionConversation, | |
| pass_str_no_const}; | |
| pam_handle_t* local_auth_handle = nullptr; // this gets set by pam_start | |
| int retval = pam_start("webserver", username.c_str(), &local_conversation, | |
| &local_auth_handle); | |
| if (retval != PAM_SUCCESS) { | |
| return retval; | |
| } | |
| retval = pam_chauthtok(local_auth_handle, PAM_SILENT); | |
| if (retval != PAM_SUCCESS) { | |
| pam_end(local_auth_handle, PAM_SUCCESS); | |
| return retval; | |
| } | |
| return pam_end(local_auth_handle, PAM_SUCCESS); | |
| } | |
| #endif // THIRD_PARTY_GBMCWEB_INCLUDE_PAM_AUTHENTICATE_H_ |