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