1 #pragma once 2 3 #include <security/pam_appl.h> 4 5 #include <boost/utility/string_view.hpp> 6 7 #include <cstring> 8 #include <memory> 9 10 // function used to get user input 11 inline int pamFunctionConversation(int numMsg, const struct pam_message** msg, 12 struct pam_response** resp, void* appdataPtr) 13 { 14 if (appdataPtr == nullptr) 15 { 16 return PAM_AUTH_ERR; 17 } 18 char* appPass = reinterpret_cast<char*>(appdataPtr); 19 size_t appPassSize = std::strlen(appPass); 20 char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1)); 21 if (pass == nullptr) 22 { 23 return PAM_AUTH_ERR; 24 } 25 26 std::strncpy(pass, appPass, appPassSize + 1); 27 28 *resp = reinterpret_cast<pam_response*>( 29 calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response))); 30 31 if (resp == nullptr) 32 { 33 free(pass); 34 return PAM_AUTH_ERR; 35 } 36 37 for (int i = 0; i < numMsg; ++i) 38 { 39 /* Ignore all PAM messages except prompting for hidden input */ 40 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 41 { 42 continue; 43 } 44 45 /* Assume PAM is only prompting for the password as hidden input */ 46 resp[i]->resp = pass; 47 } 48 49 return PAM_SUCCESS; 50 } 51 52 /** 53 * @brief Attempt username/password authentication via PAM. 54 * @param username The provided username aka account name. 55 * @param password The provided password. 56 * @returns PAM error code or PAM_SUCCESS for success. */ 57 inline int pamAuthenticateUser(const std::string_view username, 58 const std::string_view password) 59 { 60 std::string userStr(username); 61 std::string passStr(password); 62 const struct pam_conv localConversation = { 63 pamFunctionConversation, const_cast<char*>(passStr.c_str())}; 64 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 65 66 int retval = pam_start("webserver", userStr.c_str(), &localConversation, 67 &localAuthHandle); 68 if (retval != PAM_SUCCESS) 69 { 70 return retval; 71 } 72 73 retval = pam_authenticate(localAuthHandle, 74 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); 75 if (retval != PAM_SUCCESS) 76 { 77 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 78 return retval; 79 } 80 81 /* check that the account is healthy */ 82 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK); 83 if (retval != PAM_SUCCESS) 84 { 85 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 86 return retval; 87 } 88 89 return pam_end(localAuthHandle, PAM_SUCCESS); 90 } 91 92 inline int pamUpdatePassword(const std::string& username, 93 const std::string& password) 94 { 95 const struct pam_conv localConversation = { 96 pamFunctionConversation, const_cast<char*>(password.c_str())}; 97 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 98 99 int retval = pam_start("webserver", username.c_str(), &localConversation, 100 &localAuthHandle); 101 102 if (retval != PAM_SUCCESS) 103 { 104 return retval; 105 } 106 107 retval = pam_chauthtok(localAuthHandle, PAM_SILENT); 108 if (retval != PAM_SUCCESS) 109 { 110 pam_end(localAuthHandle, PAM_SUCCESS); 111 return retval; 112 } 113 114 return pam_end(localAuthHandle, PAM_SUCCESS); 115 } 116