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