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