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