blob: 7af28ad987aa2fa898f16c54e9d2a12edbc43612 [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
5#include <boost/utility/string_view.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05006
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 Tanousf3d847c2017-06-12 16:01:42 -070010
11// function used to get user input
Ed Tanous55c7b7a2018-05-22 15:27:24 -070012inline int pamFunctionConversation(int numMsg, const struct pam_message** msg,
Ed Tanous1abe55e2018-09-05 08:30:59 -070013 struct pam_response** resp, void* appdataPtr)
14{
Patrick Williamsad7fa902023-05-10 19:57:29 -050015 if ((appdataPtr == nullptr) || (msg == nullptr) || (resp == nullptr))
Ed Tanous1abe55e2018-09-05 08:30:59 -070016 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053017 return PAM_CONV_ERR;
Ed Tanous1abe55e2018-09-05 08:30:59 -070018 }
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053019
20 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
Ed Tanousf1eebf02019-03-04 15:57:09 -080021 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053022 return PAM_CONV_ERR;
Ed Tanousf1eebf02019-03-04 15:57:09 -080023 }
24
Patrick Williamsad7fa902023-05-10 19:57:29 -050025 auto msgCount = static_cast<size_t>(numMsg);
26 auto messages = std::span(msg, msgCount);
27 auto responses = std::span(resp, msgCount);
28
29 for (size_t i = 0; i < msgCount; ++i)
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 {
31 /* Ignore all PAM messages except prompting for hidden input */
Patrick Williamsad7fa902023-05-10 19:57:29 -050032 if (messages[i]->msg_style != PAM_PROMPT_ECHO_OFF)
Ed Tanous1abe55e2018-09-05 08:30:59 -070033 {
34 continue;
35 }
36
37 /* Assume PAM is only prompting for the password as hidden input */
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053038 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
39
Ed Tanous46ff87b2022-01-07 09:25:51 -080040 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053041 char* appPass = reinterpret_cast<char*>(appdataPtr);
42 size_t appPassSize = std::strlen(appPass);
43
44 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE)
45 {
46 return PAM_CONV_ERR;
47 }
Ed Tanous46ff87b2022-01-07 09:25:51 -080048 // IDeally we'd like to avoid using malloc here, but because we're
49 // passing off ownership of this to a C application, there aren't a lot
50 // of sane ways to avoid it.
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053051
Ed Tanous46ff87b2022-01-07 09:25:51 -080052 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)'
53 void* passPtr = malloc(appPassSize + 1);
54 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
55 char* pass = reinterpret_cast<char*>(passPtr);
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053056 if (pass == nullptr)
57 {
58 return PAM_BUF_ERR;
59 }
60
61 std::strncpy(pass, appPass, appPassSize + 1);
62
Ed Tanous46ff87b2022-01-07 09:25:51 -080063 size_t numMsgSize = static_cast<size_t>(numMsg);
Ed Tanousfcc5aa62022-01-07 09:40:43 -080064 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
Ed Tanous46ff87b2022-01-07 09:25:51 -080065 void* ptr = calloc(numMsgSize, sizeof(struct pam_response));
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053066 if (ptr == nullptr)
67 {
Ed Tanousfcc5aa62022-01-07 09:40:43 -080068 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053069 free(pass);
70 return PAM_BUF_ERR;
71 }
72
Ed Tanous46ff87b2022-01-07 09:25:51 -080073 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053074 *resp = reinterpret_cast<pam_response*>(ptr);
Ed Tanous46ff87b2022-01-07 09:25:51 -080075
Patrick Williamsad7fa902023-05-10 19:57:29 -050076 responses[i]->resp = pass;
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053077
78 return PAM_SUCCESS;
Ed Tanous911ac312017-08-15 09:37:42 -070079 }
Ed Tanousf3d847c2017-06-12 16:01:42 -070080
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053081 return PAM_CONV_ERR;
Ed Tanousf3d847c2017-06-12 16:01:42 -070082}
83
Joseph Reynoldsd887fff2020-01-14 16:34:09 -060084/**
85 * @brief Attempt username/password authentication via PAM.
86 * @param username The provided username aka account name.
87 * @param password The provided password.
88 * @returns PAM error code or PAM_SUCCESS for success. */
Ed Tanous26ccae32023-02-16 10:28:44 -080089inline int pamAuthenticateUser(std::string_view username,
90 std::string_view password)
Ed Tanous1abe55e2018-09-05 08:30:59 -070091{
92 std::string userStr(username);
93 std::string passStr(password);
Ed Tanous4ecc6182022-01-07 09:36:26 -080094
95 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
96 char* passStrNoConst = const_cast<char*>(passStr.c_str());
97 const struct pam_conv localConversation = {pamFunctionConversation,
98 passStrNoConst};
Ed Tanous99131cd2019-10-24 11:12:47 -070099 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousf3d847c2017-06-12 16:01:42 -0700100
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600101 int retval = pam_start("webserver", userStr.c_str(), &localConversation,
102 &localAuthHandle);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 if (retval != PAM_SUCCESS)
104 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600105 return retval;
106 }
107
108 retval = pam_authenticate(localAuthHandle,
109 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
110 if (retval != PAM_SUCCESS)
111 {
112 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
113 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 }
Ed Tanous911ac312017-08-15 09:37:42 -0700115
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 /* check that the account is healthy */
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600117 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
118 if (retval != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600120 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
121 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 }
Ed Tanous911ac312017-08-15 09:37:42 -0700123
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600124 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanous911ac312017-08-15 09:37:42 -0700125}
Ed Tanousa8408792018-09-05 16:08:38 -0700126
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000127inline int pamUpdatePassword(const std::string& username,
128 const std::string& password)
Ed Tanousa8408792018-09-05 16:08:38 -0700129{
Ed Tanous4ecc6182022-01-07 09:36:26 -0800130 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
131 char* passStrNoConst = const_cast<char*>(password.c_str());
132 const struct pam_conv localConversation = {pamFunctionConversation,
133 passStrNoConst};
Ed Tanous99131cd2019-10-24 11:12:47 -0700134 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousa8408792018-09-05 16:08:38 -0700135
Joseph Reynolds96b39e02019-12-05 17:53:35 -0600136 int retval = pam_start("webserver", username.c_str(), &localConversation,
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000137 &localAuthHandle);
Ed Tanousa8408792018-09-05 16:08:38 -0700138
139 if (retval != PAM_SUCCESS)
140 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000141 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700142 }
143
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000144 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
145 if (retval != PAM_SUCCESS)
Ed Tanousa8408792018-09-05 16:08:38 -0700146 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000147 pam_end(localAuthHandle, PAM_SUCCESS);
148 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700149 }
150
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000151 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanousa8408792018-09-05 16:08:38 -0700152}