blob: 7afe9520393878a46b134bcfea15bdf6bd19d877 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous911ac312017-08-15 09:37:42 -07003#pragma once
4
Ed Tanousf3d847c2017-06-12 16:01:42 -07005#include <security/pam_appl.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07006
Ed Tanous911ac312017-08-15 09:37:42 -07007#include <cstring>
Ed Tanouse0d918b2018-03-27 17:41:04 -07008#include <memory>
Patrick Williamsad7fa902023-05-10 19:57:29 -05009#include <span>
Ed Tanous5b904292024-04-16 11:10:17 -070010#include <string_view>
Ed Tanousf3d847c2017-06-12 16:01:42 -070011
Ravi Teja2ccce1f2024-08-10 04:05:36 -050012struct PasswordData
13{
Abhilash Rajud193e002024-08-26 00:27:01 -050014 struct Response
15 {
16 std::string_view prompt;
17 std::string value;
18 };
19
20 std::vector<Response> responseData;
21
22 int addPrompt(std::string_view prompt, std::string_view value)
23 {
24 if (value.size() + 1 > PAM_MAX_MSG_SIZE)
25 {
26 BMCWEB_LOG_ERROR("value length error", prompt);
27 return PAM_CONV_ERR;
28 }
29 responseData.emplace_back(prompt, std::string(value));
30 return PAM_SUCCESS;
31 }
Ed Tanous82f49fa2024-08-27 11:37:09 -070032
33 int makeResponse(const pam_message& msg, pam_response& response)
34 {
35 switch (msg.msg_style)
36 {
37 case PAM_PROMPT_ECHO_ON:
38 break;
39 case PAM_PROMPT_ECHO_OFF:
40 {
Abhilash Rajud193e002024-08-26 00:27:01 -050041 std::string prompt(msg.msg);
42 auto iter = std::ranges::find_if(
43 responseData, [&prompt](const Response& data) {
44 return prompt.starts_with(data.prompt);
45 });
46 if (iter == responseData.end())
Ed Tanous82f49fa2024-08-27 11:37:09 -070047 {
48 return PAM_CONV_ERR;
49 }
Abhilash Rajud193e002024-08-26 00:27:01 -050050 response.resp = strdup(iter->value.c_str());
51 return PAM_SUCCESS;
Ed Tanous82f49fa2024-08-27 11:37:09 -070052 }
53 break;
54 case PAM_ERROR_MSG:
55 {
56 BMCWEB_LOG_ERROR("Pam error {}", msg.msg);
57 }
58 break;
59 case PAM_TEXT_INFO:
60 {
61 BMCWEB_LOG_ERROR("Pam info {}", msg.msg);
62 }
63 break;
64 default:
65 {
66 return PAM_CONV_ERR;
67 }
68 }
Ed Tanous82f49fa2024-08-27 11:37:09 -070069 return PAM_SUCCESS;
70 }
Ravi Teja2ccce1f2024-08-10 04:05:36 -050071};
72
Ed Tanousf3d847c2017-06-12 16:01:42 -070073// function used to get user input
Ed Tanous05ecd3a2024-02-16 08:13:57 -080074inline int pamFunctionConversation(int numMsg, const struct pam_message** msgs,
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 struct pam_response** resp, void* appdataPtr)
76{
Ed Tanous05ecd3a2024-02-16 08:13:57 -080077 if ((appdataPtr == nullptr) || (msgs == nullptr) || (resp == nullptr))
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053079 return PAM_CONV_ERR;
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 }
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053081
82 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
Ed Tanousf1eebf02019-03-04 15:57:09 -080083 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053084 return PAM_CONV_ERR;
Ed Tanousf1eebf02019-03-04 15:57:09 -080085 }
Ravi Teja2ccce1f2024-08-10 04:05:36 -050086 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
87 PasswordData* appPass = reinterpret_cast<PasswordData*>(appdataPtr);
Patrick Williamsad7fa902023-05-10 19:57:29 -050088 auto msgCount = static_cast<size_t>(numMsg);
Ed Tanous05ecd3a2024-02-16 08:13:57 -080089 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
90 auto responseArrPtr = std::make_unique<pam_response[]>(msgCount);
91 auto responses = std::span(responseArrPtr.get(), msgCount);
92 auto messagePtrs = std::span(msgs, msgCount);
Patrick Williamsad7fa902023-05-10 19:57:29 -050093 for (size_t i = 0; i < msgCount; ++i)
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 {
Ed Tanous05ecd3a2024-02-16 08:13:57 -080095 const pam_message& msg = *(messagePtrs[i]);
96
97 pam_response& response = responses[i];
98 response.resp_retcode = 0;
99 response.resp = nullptr;
100
Ed Tanous82f49fa2024-08-27 11:37:09 -0700101 int r = appPass->makeResponse(msg, response);
102 if (r != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanous82f49fa2024-08-27 11:37:09 -0700104 return r;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 }
Ed Tanous911ac312017-08-15 09:37:42 -0700106 }
Ed Tanousf3d847c2017-06-12 16:01:42 -0700107
Ed Tanous05ecd3a2024-02-16 08:13:57 -0800108 *resp = responseArrPtr.release();
109 return PAM_SUCCESS;
Ed Tanousf3d847c2017-06-12 16:01:42 -0700110}
111
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600112/**
113 * @brief Attempt username/password authentication via PAM.
114 * @param username The provided username aka account name.
115 * @param password The provided password.
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500116 * @param token The provided MFA token.
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600117 * @returns PAM error code or PAM_SUCCESS for success. */
Ed Tanous26ccae32023-02-16 10:28:44 -0800118inline int pamAuthenticateUser(std::string_view username,
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500119 std::string_view password,
120 std::optional<std::string> token)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121{
122 std::string userStr(username);
Abhilash Rajud193e002024-08-26 00:27:01 -0500123 PasswordData data;
124 if (int ret = data.addPrompt("Password: ", password); ret != PAM_SUCCESS)
125 {
126 return ret;
127 }
128 if (token)
129 {
130 if (int ret = data.addPrompt("Verification code: ", *token);
131 ret != PAM_SUCCESS)
132 {
133 return ret;
134 }
135 }
136
Ravi Teja2ccce1f2024-08-10 04:05:36 -0500137 const struct pam_conv localConversation = {pamFunctionConversation, &data};
Ed Tanous99131cd2019-10-24 11:12:47 -0700138 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousf3d847c2017-06-12 16:01:42 -0700139
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600140 int retval = pam_start("webserver", userStr.c_str(), &localConversation,
141 &localAuthHandle);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 if (retval != PAM_SUCCESS)
143 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600144 return retval;
145 }
146
147 retval = pam_authenticate(localAuthHandle,
148 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
149 if (retval != PAM_SUCCESS)
150 {
151 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
152 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 }
Ed Tanous911ac312017-08-15 09:37:42 -0700154
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 /* check that the account is healthy */
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600156 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
157 if (retval != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600159 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
160 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 }
Ed Tanous911ac312017-08-15 09:37:42 -0700162
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600163 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanous911ac312017-08-15 09:37:42 -0700164}
Ed Tanousa8408792018-09-05 16:08:38 -0700165
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000166inline int pamUpdatePassword(const std::string& username,
167 const std::string& password)
Ed Tanousa8408792018-09-05 16:08:38 -0700168{
Abhilash Rajud193e002024-08-26 00:27:01 -0500169 PasswordData data;
170 if (int ret = data.addPrompt("New password: ", password);
171 ret != PAM_SUCCESS)
172 {
173 return ret;
174 }
175 if (int ret = data.addPrompt("Retype new password: ", password);
176 ret != PAM_SUCCESS)
177 {
178 return ret;
179 }
180 const struct pam_conv localConversation = {pamFunctionConversation, &data};
Ed Tanous99131cd2019-10-24 11:12:47 -0700181 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousa8408792018-09-05 16:08:38 -0700182
Joseph Reynolds96b39e02019-12-05 17:53:35 -0600183 int retval = pam_start("webserver", username.c_str(), &localConversation,
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000184 &localAuthHandle);
Ed Tanousa8408792018-09-05 16:08:38 -0700185
186 if (retval != PAM_SUCCESS)
187 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000188 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700189 }
190
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000191 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
192 if (retval != PAM_SUCCESS)
Ed Tanousa8408792018-09-05 16:08:38 -0700193 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000194 pam_end(localAuthHandle, PAM_SUCCESS);
195 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700196 }
197
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000198 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanousa8408792018-09-05 16:08:38 -0700199}