1 #pragma once 2 3 #include <security/pam_appl.h> 4 5 #include <cstring> 6 #include <memory> 7 #include <span> 8 #include <string_view> 9 10 // function used to get user input 11 inline int pamFunctionConversation(int numMsg, const struct pam_message** msgs, 12 struct pam_response** resp, void* appdataPtr) 13 { 14 if ((appdataPtr == nullptr) || (msgs == nullptr) || (resp == 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 auto msgCount = static_cast<size_t>(numMsg); 24 25 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) 26 auto responseArrPtr = std::make_unique<pam_response[]>(msgCount); 27 auto responses = std::span(responseArrPtr.get(), msgCount); 28 auto messagePtrs = std::span(msgs, msgCount); 29 for (size_t i = 0; i < msgCount; ++i) 30 { 31 const pam_message& msg = *(messagePtrs[i]); 32 33 pam_response& response = responses[i]; 34 response.resp_retcode = 0; 35 response.resp = nullptr; 36 37 switch (msg.msg_style) 38 { 39 case PAM_PROMPT_ECHO_ON: 40 break; 41 case PAM_PROMPT_ECHO_OFF: 42 { 43 // Assume PAM is only prompting for the password as hidden input 44 // Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred 45 char* appPass = static_cast<char*>(appdataPtr); 46 size_t appPassSize = std::strlen(appPass); 47 48 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE) 49 { 50 return PAM_CONV_ERR; 51 } 52 // Create an array for pam to own 53 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) 54 auto passPtr = std::make_unique<char[]>(appPassSize + 1); 55 std::strncpy(passPtr.get(), appPass, appPassSize + 1); 56 57 responses[i].resp = passPtr.release(); 58 } 59 break; 60 case PAM_ERROR_MSG: 61 BMCWEB_LOG_ERROR("Pam error {}", msg.msg); 62 break; 63 case PAM_TEXT_INFO: 64 BMCWEB_LOG_ERROR("Pam info {}", msg.msg); 65 break; 66 default: 67 return PAM_CONV_ERR; 68 } 69 } 70 71 *resp = responseArrPtr.release(); 72 return PAM_SUCCESS; 73 } 74 75 /** 76 * @brief Attempt username/password authentication via PAM. 77 * @param username The provided username aka account name. 78 * @param password The provided password. 79 * @returns PAM error code or PAM_SUCCESS for success. */ 80 inline int pamAuthenticateUser(std::string_view username, 81 std::string_view password) 82 { 83 std::string userStr(username); 84 std::string passStr(password); 85 86 char* passStrNoConst = passStr.data(); 87 const struct pam_conv localConversation = {pamFunctionConversation, 88 passStrNoConst}; 89 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 90 91 int retval = pam_start("webserver", userStr.c_str(), &localConversation, 92 &localAuthHandle); 93 if (retval != PAM_SUCCESS) 94 { 95 return retval; 96 } 97 98 retval = pam_authenticate(localAuthHandle, 99 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); 100 if (retval != PAM_SUCCESS) 101 { 102 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 103 return retval; 104 } 105 106 /* check that the account is healthy */ 107 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK); 108 if (retval != PAM_SUCCESS) 109 { 110 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval 111 return retval; 112 } 113 114 return pam_end(localAuthHandle, PAM_SUCCESS); 115 } 116 117 inline int pamUpdatePassword(const std::string& username, 118 const std::string& password) 119 { 120 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) 121 char* passStrNoConst = const_cast<char*>(password.c_str()); 122 const struct pam_conv localConversation = {pamFunctionConversation, 123 passStrNoConst}; 124 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start 125 126 int retval = pam_start("webserver", username.c_str(), &localConversation, 127 &localAuthHandle); 128 129 if (retval != PAM_SUCCESS) 130 { 131 return retval; 132 } 133 134 retval = pam_chauthtok(localAuthHandle, PAM_SILENT); 135 if (retval != PAM_SUCCESS) 136 { 137 pam_end(localAuthHandle, PAM_SUCCESS); 138 return retval; 139 } 140 141 return pam_end(localAuthHandle, PAM_SUCCESS); 142 } 143