1 #pragma once
2 
3 #include <security/pam_appl.h>
4 
5 #include <boost/utility/string_view.hpp>
6 #include <cstring>
7 #include <memory>
8 
9 // function used to get user input
10 inline int pamFunctionConversation(int numMsg, const struct pam_message** msg,
11                                    struct pam_response** resp, void* appdataPtr)
12 {
13     if (appdataPtr == nullptr)
14     {
15         return PAM_AUTH_ERR;
16     }
17     char* appPass = reinterpret_cast<char*>(appdataPtr);
18     size_t appPassSize = std::strlen(appPass);
19     char* pass = reinterpret_cast<char*>(malloc(appPassSize + 1));
20     if (pass == nullptr)
21     {
22         return PAM_AUTH_ERR;
23     }
24 
25     std::strcpy(pass, appPass);
26 
27     *resp = reinterpret_cast<pam_response*>(
28         calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response)));
29 
30     if (resp == nullptr)
31     {
32         return PAM_AUTH_ERR;
33     }
34 
35     for (int i = 0; i < numMsg; ++i)
36     {
37         /* Ignore all PAM messages except prompting for hidden input */
38         if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
39         {
40             continue;
41         }
42 
43         /* Assume PAM is only prompting for the password as hidden input */
44         resp[i]->resp = pass;
45     }
46 
47     return PAM_SUCCESS;
48 }
49 
50 /**
51  * @brief Attempt username/password authentication via PAM.
52  * @param username The provided username aka account name.
53  * @param password The provided password.
54  * @returns PAM error code or PAM_SUCCESS for success. */
55 inline int pamAuthenticateUser(const std::string_view username,
56                                const std::string_view password)
57 {
58     std::string userStr(username);
59     std::string passStr(password);
60     const struct pam_conv localConversation = {
61         pamFunctionConversation, const_cast<char*>(passStr.c_str())};
62     pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
63 
64     int retval = pam_start("webserver", userStr.c_str(), &localConversation,
65                            &localAuthHandle);
66     if (retval != PAM_SUCCESS)
67     {
68         return retval;
69     }
70 
71     retval = pam_authenticate(localAuthHandle,
72                               PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
73     if (retval != PAM_SUCCESS)
74     {
75         pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
76         return retval;
77     }
78 
79     /* check that the account is healthy */
80     retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
81     if (retval != PAM_SUCCESS)
82     {
83         pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
84         return retval;
85     }
86 
87     return pam_end(localAuthHandle, PAM_SUCCESS);
88 }
89 
90 inline int pamUpdatePassword(const std::string& username,
91                              const std::string& password)
92 {
93     const struct pam_conv localConversation = {
94         pamFunctionConversation, const_cast<char*>(password.c_str())};
95     pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
96 
97     int retval = pam_start("webserver", username.c_str(), &localConversation,
98                            &localAuthHandle);
99 
100     if (retval != PAM_SUCCESS)
101     {
102         return retval;
103     }
104 
105     retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
106     if (retval != PAM_SUCCESS)
107     {
108         pam_end(localAuthHandle, PAM_SUCCESS);
109         return retval;
110     }
111 
112     return pam_end(localAuthHandle, PAM_SUCCESS);
113 }
114