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