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 return PAM_AUTH_ERR; 34 } 35 36 for (int i = 0; i < numMsg; ++i) 37 { 38 /* Ignore all PAM messages except prompting for hidden input */ 39 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 40 { 41 continue; 42 } 43 44 /* Assume PAM is only prompting for the password as hidden input */ 45 resp[i]->resp = pass; 46 } 47 48 return PAM_SUCCESS; 49 } 50 51 /** 52 * @brief Attempt username/password authentication via PAM. 53 * @param username The provided username aka account name. 54 * @param password The provided password. 55 * @returns PAM error code or PAM_SUCCESS for success. */ 56 inline int pamAuthenticateUser(const std::string_view username, 57 const std::string_view password) 58 { 59 std::string userStr(username); 60 std::string passStr(password); 61 const struct pam_conv localConversation = { 62 pamFunctionConversation, const_cast<char*>(passStr.c_str())}; 63 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 64 65 int retval = pam_start("webserver", userStr.c_str(), &localConversation, 66 &localAuthHandle); 67 if (retval != PAM_SUCCESS) 68 { 69 return retval; 70 } 71 72 retval = pam_authenticate(localAuthHandle, 73 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); 74 if (retval != PAM_SUCCESS) 75 { 76 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 77 return retval; 78 } 79 80 /* check that the account is healthy */ 81 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK); 82 if (retval != PAM_SUCCESS) 83 { 84 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 85 return retval; 86 } 87 88 return pam_end(localAuthHandle, PAM_SUCCESS); 89 } 90 91 inline int pamUpdatePassword(const std::string& username, 92 const std::string& password) 93 { 94 const struct pam_conv localConversation = { 95 pamFunctionConversation, const_cast<char*>(password.c_str())}; 96 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 97 98 int retval = pam_start("webserver", username.c_str(), &localConversation, 99 &localAuthHandle); 100 101 if (retval != PAM_SUCCESS) 102 { 103 return retval; 104 } 105 106 retval = pam_chauthtok(localAuthHandle, PAM_SILENT); 107 if (retval != PAM_SUCCESS) 108 { 109 pam_end(localAuthHandle, PAM_SUCCESS); 110 return retval; 111 } 112 113 return pam_end(localAuthHandle, PAM_SUCCESS); 114 } 115