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