blob: 9854df6150414a1e8725bf0b43651e131e7e5597 [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
10// function used to get user input
Ed Tanous55c7b7a2018-05-22 15:27:24 -070011inline int pamFunctionConversation(int numMsg, const struct pam_message** msg,
Ed Tanous1abe55e2018-09-05 08:30:59 -070012 struct pam_response** resp, void* appdataPtr)
13{
Patrick Williamsad7fa902023-05-10 19:57:29 -050014 if ((appdataPtr == nullptr) || (msg == nullptr) || (resp == nullptr))
Ed Tanous1abe55e2018-09-05 08:30:59 -070015 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053016 return PAM_CONV_ERR;
Ed Tanous1abe55e2018-09-05 08:30:59 -070017 }
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053018
19 if (numMsg <= 0 || numMsg >= PAM_MAX_NUM_MSG)
Ed Tanousf1eebf02019-03-04 15:57:09 -080020 {
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053021 return PAM_CONV_ERR;
Ed Tanousf1eebf02019-03-04 15:57:09 -080022 }
23
Patrick Williamsad7fa902023-05-10 19:57:29 -050024 auto msgCount = static_cast<size_t>(numMsg);
25 auto messages = std::span(msg, msgCount);
26 auto responses = std::span(resp, msgCount);
27
28 for (size_t i = 0; i < msgCount; ++i)
Ed Tanous1abe55e2018-09-05 08:30:59 -070029 {
30 /* Ignore all PAM messages except prompting for hidden input */
Patrick Williamsad7fa902023-05-10 19:57:29 -050031 if (messages[i]->msg_style != PAM_PROMPT_ECHO_OFF)
Ed Tanous1abe55e2018-09-05 08:30:59 -070032 {
33 continue;
34 }
35
36 /* Assume PAM is only prompting for the password as hidden input */
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053037 /* Allocate memory only when PAM_PROMPT_ECHO_OFF is encounterred */
38
Ed Tanous46ff87b2022-01-07 09:25:51 -080039 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053040 char* appPass = reinterpret_cast<char*>(appdataPtr);
41 size_t appPassSize = std::strlen(appPass);
42
43 if ((appPassSize + 1) > PAM_MAX_RESP_SIZE)
44 {
45 return PAM_CONV_ERR;
46 }
Ed Tanous46ff87b2022-01-07 09:25:51 -080047 // IDeally we'd like to avoid using malloc here, but because we're
48 // passing off ownership of this to a C application, there aren't a lot
49 // of sane ways to avoid it.
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053050
Ed Tanous7b8270c2023-06-06 11:44:21 -070051 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
Ed Tanous46ff87b2022-01-07 09:25:51 -080052 void* passPtr = malloc(appPassSize + 1);
53 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
54 char* pass = reinterpret_cast<char*>(passPtr);
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053055 if (pass == nullptr)
56 {
57 return PAM_BUF_ERR;
58 }
59
60 std::strncpy(pass, appPass, appPassSize + 1);
61
Ed Tanous46ff87b2022-01-07 09:25:51 -080062 size_t numMsgSize = static_cast<size_t>(numMsg);
Ed Tanousfcc5aa62022-01-07 09:40:43 -080063 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
Ed Tanous46ff87b2022-01-07 09:25:51 -080064 void* ptr = calloc(numMsgSize, sizeof(struct pam_response));
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053065 if (ptr == nullptr)
66 {
Ed Tanousfcc5aa62022-01-07 09:40:43 -080067 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053068 free(pass);
69 return PAM_BUF_ERR;
70 }
71
Ed Tanous46ff87b2022-01-07 09:25:51 -080072 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053073 *resp = reinterpret_cast<pam_response*>(ptr);
Ed Tanous46ff87b2022-01-07 09:25:51 -080074
Patrick Williamsad7fa902023-05-10 19:57:29 -050075 responses[i]->resp = pass;
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053076
77 return PAM_SUCCESS;
Ed Tanous911ac312017-08-15 09:37:42 -070078 }
Ed Tanousf3d847c2017-06-12 16:01:42 -070079
P Dheeraj Srujan Kumarba95fcc2021-07-12 21:47:59 +053080 return PAM_CONV_ERR;
Ed Tanousf3d847c2017-06-12 16:01:42 -070081}
82
Joseph Reynoldsd887fff2020-01-14 16:34:09 -060083/**
84 * @brief Attempt username/password authentication via PAM.
85 * @param username The provided username aka account name.
86 * @param password The provided password.
87 * @returns PAM error code or PAM_SUCCESS for success. */
Ed Tanous26ccae32023-02-16 10:28:44 -080088inline int pamAuthenticateUser(std::string_view username,
89 std::string_view password)
Ed Tanous1abe55e2018-09-05 08:30:59 -070090{
91 std::string userStr(username);
92 std::string passStr(password);
Ed Tanous4ecc6182022-01-07 09:36:26 -080093
Ed Tanousf9c794f2023-06-06 11:46:49 -070094 char* passStrNoConst = passStr.data();
Ed Tanous4ecc6182022-01-07 09:36:26 -080095 const struct pam_conv localConversation = {pamFunctionConversation,
96 passStrNoConst};
Ed Tanous99131cd2019-10-24 11:12:47 -070097 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousf3d847c2017-06-12 16:01:42 -070098
Joseph Reynoldsd887fff2020-01-14 16:34:09 -060099 int retval = pam_start("webserver", userStr.c_str(), &localConversation,
100 &localAuthHandle);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 if (retval != PAM_SUCCESS)
102 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600103 return retval;
104 }
105
106 retval = pam_authenticate(localAuthHandle,
107 PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
108 if (retval != PAM_SUCCESS)
109 {
110 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
111 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 }
Ed Tanous911ac312017-08-15 09:37:42 -0700113
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 /* check that the account is healthy */
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600115 retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
116 if (retval != PAM_SUCCESS)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 {
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600118 pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
119 return retval;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 }
Ed Tanous911ac312017-08-15 09:37:42 -0700121
Joseph Reynoldsd887fff2020-01-14 16:34:09 -0600122 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanous911ac312017-08-15 09:37:42 -0700123}
Ed Tanousa8408792018-09-05 16:08:38 -0700124
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125inline int pamUpdatePassword(const std::string& username,
126 const std::string& password)
Ed Tanousa8408792018-09-05 16:08:38 -0700127{
Ed Tanous4ecc6182022-01-07 09:36:26 -0800128 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
129 char* passStrNoConst = const_cast<char*>(password.c_str());
130 const struct pam_conv localConversation = {pamFunctionConversation,
131 passStrNoConst};
Ed Tanous99131cd2019-10-24 11:12:47 -0700132 pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
Ed Tanousa8408792018-09-05 16:08:38 -0700133
Joseph Reynolds96b39e02019-12-05 17:53:35 -0600134 int retval = pam_start("webserver", username.c_str(), &localConversation,
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000135 &localAuthHandle);
Ed Tanousa8408792018-09-05 16:08:38 -0700136
137 if (retval != PAM_SUCCESS)
138 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000139 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700140 }
141
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000142 retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
143 if (retval != PAM_SUCCESS)
Ed Tanousa8408792018-09-05 16:08:38 -0700144 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000145 pam_end(localAuthHandle, PAM_SUCCESS);
146 return retval;
Ed Tanousa8408792018-09-05 16:08:38 -0700147 }
148
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000149 return pam_end(localAuthHandle, PAM_SUCCESS);
Ed Tanousa8408792018-09-05 16:08:38 -0700150}