blob: 7e970f0279f8dceedfe7d8d0e0fccae9121cf7c7 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#pragma once
2
Ed Tanousf3d847c2017-06-12 16:01:42 -07003#include <security/pam_appl.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07004
Ed Tanous911ac312017-08-15 09:37:42 -07005#include <cstring>
Ed Tanouse0d918b2018-03-27 17:41:04 -07006#include <memory>
Patrick Williamsad7fa902023-05-10 19:57:29 -05007#include <span>
Ed Tanous5b904292024-04-16 11:10:17 -07008#include <string_view>
Ed Tanousf3d847c2017-06-12 16:01:42 -07009
Ravi Teja2ccce1f2024-08-10 04:05:36 -050010struct PasswordData
11{
Abhilash Rajud193e002024-08-26 00:27:01 -050012 struct Response
13 {
14 std::string_view prompt;
15 std::string value;
16 };
17
18 std::vector<Response> responseData;
19
20 int addPrompt(std::string_view prompt, std::string_view value)
21 {
22 if (value.size() + 1 > PAM_MAX_MSG_SIZE)
23 {
24 BMCWEB_LOG_ERROR("value length error", prompt);
25 return PAM_CONV_ERR;
26 }
27 responseData.emplace_back(prompt, std::string(value));
28 return PAM_SUCCESS;
29 }
Ed Tanous82f49fa2024-08-27 11:37:09 -070030
31 int makeResponse(const pam_message& msg, pam_response& response)
32 {
33 switch (msg.msg_style)
34 {
35 case PAM_PROMPT_ECHO_ON:
36 break;
37 case PAM_PROMPT_ECHO_OFF:
38 {
Abhilash Rajud193e002024-08-26 00:27:01 -050039 std::string prompt(msg.msg);
40 auto iter = std::ranges::find_if(
41 responseData, [&prompt](const Response& data) {
42 return prompt.starts_with(data.prompt);
43 });
44 if (iter == responseData.end())
Ed Tanous82f49fa2024-08-27 11:37:09 -070045 {
46 return PAM_CONV_ERR;
47 }
Abhilash Rajud193e002024-08-26 00:27:01 -050048 response.resp = strdup(iter->value.c_str());
49 return PAM_SUCCESS;
Ed Tanous82f49fa2024-08-27 11:37:09 -070050 }
51 break;
52 case PAM_ERROR_MSG:
53 {
54 BMCWEB_LOG_ERROR("Pam error {}", msg.msg);
55 }
56 break;
57 case PAM_TEXT_INFO:
58 {
59 BMCWEB_LOG_ERROR("Pam info {}", msg.msg);
60 }
61 break;
62 default:
63 {
64 return PAM_CONV_ERR;
65 }
66 }
Ed Tanous82f49fa2024-08-27 11:37:09 -070067 return PAM_SUCCESS;
68 }
Ravi Teja2ccce1f2024-08-10 04:05:36 -050069};
70
Ed Tanousf3d847c2017-06-12 16:01:42 -070071// function used to get user input
Ed Tanous05ecd3a2024-02-16 08:13:57 -080072inline int pamFunctionConversation(int numMsg, const struct pam_message** msgs,
Ed Tanous1abe55e2018-09-05 08:30:59 -070073 struct pam_response** resp, void* appdataPtr)
74{
Ed Tanous05ecd3a2024-02-16 08:13:57 -080075 if ((appdataPtr == nullptr) || (msgs == nullptr) || (resp == nullptr))
Ed Tanous1abe55e2018-09-05 08:30:59 -070076 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053077 return PAM_CONV_ERR;
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 }
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053079
80 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
Ed Tanousf1eebf02019-03-04 15:57:09 -080081 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053082 return PAM_CONV_ERR;
Ed Tanousf1eebf02019-03-04 15:57:09 -080083 }
Ravi Teja2ccce1f2024-08-10 04:05:36 -050084 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
85 PasswordData* appPass = reinterpret_cast<PasswordData*>(appdataPtr);
Patrick Williamsad7fa902023-05-10 19:57:29 -050086 auto msgCount = static_cast<size_t>(numMsg);
Ed Tanous05ecd3a2024-02-16 08:13:57 -080087 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
88 auto responseArrPtr = std::make_unique<pam_response[]>(msgCount);
89 auto responses = std::span(responseArrPtr.get(), msgCount);
90 auto messagePtrs = std::span(msgs, msgCount);
Patrick Williamsad7fa902023-05-10 19:57:29 -050091 for (size_t i = 0; i < msgCount; ++i)
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 {
Ed Tanous05ecd3a2024-02-16 08:13:57 -080093 const pam_message& msg = *(messagePtrs[i]);
94
95 pam_response& response = responses[i];
96 response.resp_retcode = 0;
97 response.resp = nullptr;
98
Ed Tanous82f49fa2024-08-27 11:37:09 -070099 int r = appPass->makeResponse(msg, response);
100 if (r != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 {
Ed Tanous82f49fa2024-08-27 11:37:09 -0700102 return r;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 }
Ed Tanous911ac312017-08-15 09:37:42 -0700104 }
Ed Tanousf3d847c2017-06-12 16:01:42 -0700105
Ed Tanous05ecd3a2024-02-16 08:13:57 -0800106 *resp = responseArrPtr.release();
107 return PAM_SUCCESS;
Ed Tanousf3d847c2017-06-12 16:01:42 -0700108}
109
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600110/**
111 * @brief Attempt username/password authentication via PAM.
112 * @param username The provided username aka account name.
113 * @param password The provided password.
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500114 * @param token The provided MFA token.
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600115 * @returns PAM error code or PAM_SUCCESS for success. */
Ed Tanous26ccae32023-02-16 10:28:44 -0800116inline int pamAuthenticateUser(std::string_view username,
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500117 std::string_view password,
118 std::optional<std::string> token)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119{
120 std::string userStr(username);
Abhilash Rajud193e002024-08-26 00:27:01 -0500121 PasswordData data;
122 if (int ret = data.addPrompt("Password: ", password); ret != PAM_SUCCESS)
123 {
124 return ret;
125 }
126 if (token)
127 {
128 if (int ret = data.addPrompt("Verification code: ", *token);
129 ret != PAM_SUCCESS)
130 {
131 return ret;
132 }
133 }
134
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500135 const struct pam_conv localConversation = {pamFunctionConversation, &data};
Ed Tanous99131cd2019-10-24 11:12:47 -0700136 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousf3d847c2017-06-12 16:01:42 -0700137
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600138 int retval = pam_start("webserver", userStr.c_str(), &localConversation,
139 &localAuthHandle);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700140 if (retval != PAM_SUCCESS)
141 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600142 return retval;
143 }
144
145 retval = pam_authenticate(localAuthHandle,
146 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
147 if (retval != PAM_SUCCESS)
148 {
149 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
150 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 }
Ed Tanous911ac312017-08-15 09:37:42 -0700152
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 /* check that the account is healthy */
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600154 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
155 if (retval != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600157 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
158 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 }
Ed Tanous911ac312017-08-15 09:37:42 -0700160
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600161 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanous911ac312017-08-15 09:37:42 -0700162}
Ed Tanousa8408792018-09-05 16:08:38 -0700163
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000164inline int pamUpdatePassword(const std::string& username,
165 const std::string& password)
Ed Tanousa8408792018-09-05 16:08:38 -0700166{
Abhilash Rajud193e002024-08-26 00:27:01 -0500167 PasswordData data;
168 if (int ret = data.addPrompt("New password: ", password);
169 ret != PAM_SUCCESS)
170 {
171 return ret;
172 }
173 if (int ret = data.addPrompt("Retype new password: ", password);
174 ret != PAM_SUCCESS)
175 {
176 return ret;
177 }
178 const struct pam_conv localConversation = {pamFunctionConversation, &data};
Ed Tanous99131cd2019-10-24 11:12:47 -0700179 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousa8408792018-09-05 16:08:38 -0700180
Joseph Reynolds96b39e02019-12-05 17:53:35 -0600181 int retval = pam_start("webserver", username.c_str(), &localConversation,
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000182 &localAuthHandle);
Ed Tanousa8408792018-09-05 16:08:38 -0700183
184 if (retval != PAM_SUCCESS)
185 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000186 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700187 }
188
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000189 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
190 if (retval != PAM_SUCCESS)
Ed Tanousa8408792018-09-05 16:08:38 -0700191 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000192 pam_end(localAuthHandle, PAM_SUCCESS);
193 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700194 }
195
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000196 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanousa8408792018-09-05 16:08:38 -0700197}