pam_authenticate.hpp (05ecd3a9a2bb7d63470a183e23b3a158572003f4) pam_authenticate.hpp (2ccce1f3a04ab72824820a6795878ce01f37a22c)
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
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
10struct PasswordData
11{
12 std::string password;
13 std::optional<std::string> token;
14};
15
10// function used to get user input
11inline 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 }
16// function used to get user input
17inline int pamFunctionConversation(int numMsg, const struct pam_message** msgs,
18 struct pam_response** resp, void* appdataPtr)
19{
20 if ((appdataPtr == nullptr) || (msgs == nullptr) || (resp == nullptr))
21 {
22 return PAM_CONV_ERR;
23 }
24
25 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
26 {
27 return PAM_CONV_ERR;
28 }
29 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
30 PasswordData* appPass = reinterpret_cast<PasswordData*>(appdataPtr);
23 auto msgCount = static_cast<size_t>(numMsg);
31 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
32 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
33 auto responseArrPtr = std::make_unique<pam_response[]>(msgCount);
34 auto responses = std::span(responseArrPtr.get(), msgCount);
35 auto messagePtrs = std::span(msgs, msgCount);
36 for (size_t i = 0; i < msgCount; ++i)
37 {
38 const pam_message& msg = *(messagePtrs[i]);
39
40 pam_response& response = responses[i];
41 response.resp_retcode = 0;
42 response.resp = nullptr;
43
44 switch (msg.msg_style)
45 {
46 case PAM_PROMPT_ECHO_ON:
47 break;
48 case PAM_PROMPT_ECHO_OFF:
49 {
50 // 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
51 // Allocate memory only when PAM_PROMPT_ECHO_OFF is encountered
52 size_t appPassSize = appPass->password.size();
48 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE)
49 {
50 return PAM_CONV_ERR;
51 }
53 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE)
54 {
55 return PAM_CONV_ERR;
56 }
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();
57 std::string_view message(msg.msg);
58 constexpr std::string_view passwordPrompt = "Password: ";
59 // String used by Google authenticator to ask for one time code
60 constexpr std::string_view totpPrompt = "Verification code: ";
61 if (message.starts_with(passwordPrompt))
62 {
63 response.resp =
64 strdup(appPass->password.c_str()); // Password input
65 }
66 else if (message.starts_with(totpPrompt))
67 {
68 if (!appPass->token)
69 {
70 return PAM_CONV_ERR;
71 }
72 response.resp =
73 strdup(appPass->token->c_str()); // TOTP input
74 }
75 else
76 {
77 return PAM_CONV_ERR;
78 }
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;

--- 5 unchanged lines hidden (view full) ---

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 }
80 break;
81 case PAM_ERROR_MSG:
82 BMCWEB_LOG_ERROR("Pam error {}", msg.msg);
83 break;
84 case PAM_TEXT_INFO:
85 BMCWEB_LOG_ERROR("Pam info {}", msg.msg);
86 break;

--- 5 unchanged lines hidden (view full) ---

92 *resp = responseArrPtr.release();
93 return PAM_SUCCESS;
94}
95
96/**
97 * @brief Attempt username/password authentication via PAM.
98 * @param username The provided username aka account name.
99 * @param password The provided password.
100 * @param token The provided MFA token.
79 * @returns PAM error code or PAM_SUCCESS for success. */
80inline int pamAuthenticateUser(std::string_view username,
101 * @returns PAM error code or PAM_SUCCESS for success. */
102inline int pamAuthenticateUser(std::string_view username,
81 std::string_view password)
103 std::string_view password,
104 std::optional<std::string> token)
82{
83 std::string userStr(username);
105{
106 std::string userStr(username);
84 std::string passStr(password);
85
86 char* passStrNoConst = passStr.data();
87 const struct pam_conv localConversation = {pamFunctionConversation,
88 passStrNoConst};
107 PasswordData data{std::string(password), std::move(token)};
108 const struct pam_conv localConversation = {pamFunctionConversation, &data};
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 }

--- 46 unchanged lines hidden ---
109 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
110
111 int retval = pam_start("webserver", userStr.c_str(), &localConversation,
112 &localAuthHandle);
113 if (retval != PAM_SUCCESS)
114 {
115 return retval;
116 }

--- 46 unchanged lines hidden ---