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 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 28 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 29 { 30 continue; 31 } 32 33 /* Assume PAM is only prompting for the password as hidden input */ 34 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */ 35 36 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 37 char* appPass = reinterpret_cast<char*>(appdataPtr); 38 size_t appPassSize = std::strlen(appPass); 39 40 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE) 41 { 42 return PAM_CONV_ERR; 43 } 44 // IDeally we'd like to avoid using malloc here, but because we're 45 // passing off ownership of this to a C application, there aren't a lot 46 // of sane ways to avoid it. 47 48 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)' 49 void* passPtr = malloc(appPassSize + 1); 50 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 51 char* pass = reinterpret_cast<char*>(passPtr); 52 if (pass == nullptr) 53 { 54 return PAM_BUF_ERR; 55 } 56 57 std::strncpy(pass, appPass, appPassSize + 1); 58 59 size_t numMsgSize = static_cast<size_t>(numMsg); 60 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) 61 void* ptr = calloc(numMsgSize, sizeof(struct pam_response)); 62 if (ptr == nullptr) 63 { 64 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc) 65 free(pass); 66 return PAM_BUF_ERR; 67 } 68 69 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 70 *resp = reinterpret_cast<pam_response*>(ptr); 71 72 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 73 resp[i]->resp = pass; 74 75 return PAM_SUCCESS; 76 } 77 78 return PAM_CONV_ERR; 79 } 80 81 /** 82 * @brief Attempt username/password authentication via PAM. 83 * @param username The provided username aka account name. 84 * @param password The provided password. 85 * @returns PAM error code or PAM_SUCCESS for success. */ 86 inline int pamAuthenticateUser(const std::string_view username, 87 const std::string_view password) 88 { 89 std::string userStr(username); 90 std::string passStr(password); 91 92 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) 93 char* passStrNoConst = const_cast<char*>(passStr.c_str()); 94 const struct pam_conv localConversation = {pamFunctionConversation, 95 passStrNoConst}; 96 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 97 98 int retval = pam_start("webserver", userStr.c_str(), &localConversation, 99 &localAuthHandle); 100 if (retval != PAM_SUCCESS) 101 { 102 return retval; 103 } 104 105 retval = pam_authenticate(localAuthHandle, 106 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); 107 if (retval != PAM_SUCCESS) 108 { 109 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 110 return retval; 111 } 112 113 /* check that the account is healthy */ 114 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK); 115 if (retval != PAM_SUCCESS) 116 { 117 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 118 return retval; 119 } 120 121 return pam_end(localAuthHandle, PAM_SUCCESS); 122 } 123 124 inline int pamUpdatePassword(const std::string& username, 125 const std::string& password) 126 { 127 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) 128 char* passStrNoConst = const_cast<char*>(password.c_str()); 129 const struct pam_conv localConversation = {pamFunctionConversation, 130 passStrNoConst}; 131 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 132 133 int retval = pam_start("webserver", username.c_str(), &localConversation, 134 &localAuthHandle); 135 136 if (retval != PAM_SUCCESS) 137 { 138 return retval; 139 } 140 141 retval = pam_chauthtok(localAuthHandle, PAM_SILENT); 142 if (retval != PAM_SUCCESS) 143 { 144 pam_end(localAuthHandle, PAM_SUCCESS); 145 return retval; 146 } 147 148 return pam_end(localAuthHandle, PAM_SUCCESS); 149 } 150