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::strcpy(pass, appPass);
27 
28     *resp = reinterpret_cast<pam_response*>(
29         calloc(static_cast<size_t>(numMsg), sizeof(struct pam_response)));
30 
31     if (resp == nullptr)
32     {
33         return PAM_AUTH_ERR;
34     }
35 
36     for (int i = 0; i < numMsg; ++i)
37     {
38         /* Ignore all PAM messages except prompting for hidden input */
39         if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
40         {
41             continue;
42         }
43 
44         /* Assume PAM is only prompting for the password as hidden input */
45         resp[i]->resp = pass;
46     }
47 
48     return PAM_SUCCESS;
49 }
50 
51 /**
52  * @brief Attempt username/password authentication via PAM.
53  * @param username The provided username aka account name.
54  * @param password The provided password.
55  * @returns PAM error code or PAM_SUCCESS for success. */
56 inline int pamAuthenticateUser(const std::string_view username,
57                                const std::string_view password)
58 {
59     std::string userStr(username);
60     std::string passStr(password);
61     const struct pam_conv localConversation = {
62         pamFunctionConversation, const_cast<char*>(passStr.c_str())};
63     pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
64 
65     int retval = pam_start("webserver", userStr.c_str(), &localConversation,
66                            &localAuthHandle);
67     if (retval != PAM_SUCCESS)
68     {
69         return retval;
70     }
71 
72     retval = pam_authenticate(localAuthHandle,
73                               PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
74     if (retval != PAM_SUCCESS)
75     {
76         pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
77         return retval;
78     }
79 
80     /* check that the account is healthy */
81     retval = pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK);
82     if (retval != PAM_SUCCESS)
83     {
84         pam_end(localAuthHandle, PAM_SUCCESS); // ignore retval
85         return retval;
86     }
87 
88     return pam_end(localAuthHandle, PAM_SUCCESS);
89 }
90 
91 inline int pamUpdatePassword(const std::string& username,
92                              const std::string& password)
93 {
94     const struct pam_conv localConversation = {
95         pamFunctionConversation, const_cast<char*>(password.c_str())};
96     pam_handle_t* localAuthHandle = nullptr; // this gets set by pam_start
97 
98     int retval = pam_start("webserver", username.c_str(), &localConversation,
99                            &localAuthHandle);
100 
101     if (retval != PAM_SUCCESS)
102     {
103         return retval;
104     }
105 
106     retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
107     if (retval != PAM_SUCCESS)
108     {
109         pam_end(localAuthHandle, PAM_SUCCESS);
110         return retval;
111     }
112 
113     return pam_end(localAuthHandle, PAM_SUCCESS);
114 }
115