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 inline bool pamAuthenticateUser(const std::string_view username,
51                                 const std::string_view password)
52 {
53     std::string userStr(username);
54     std::string passStr(password);
55     const struct pam_conv localConversation = {
56         pamFunctionConversation, const_cast<char*>(passStr.c_str())};
57     pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
58 
59     if (pam_start("webserver", userStr.c_str(), &localConversation,
60                   &localAuthHandle) != PAM_SUCCESS)
61     {
62         return false;
63     }
64     int retval = pam_authenticate(localAuthHandle,
65                                   PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
66 
67     if (retval != PAM_SUCCESS)
68     {
69         pam_end(localAuthHandle, PAM_SUCCESS);
70         return false;
71     }
72 
73     /* check that the account is healthy */
74     if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
75         PAM_SUCCESS)
76     {
77         pam_end(localAuthHandle, PAM_SUCCESS);
78         return false;
79     }
80 
81     if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
82     {
83         return false;
84     }
85 
86     return true;
87 }
88 
89 inline bool pamUpdatePassword(const std::string& username,
90                               const std::string& password)
91 {
92     const struct pam_conv localConversation = {
93         pamFunctionConversation, const_cast<char*>(password.c_str())};
94     pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
95 
96     if (pam_start("passwd", username.c_str(), &localConversation,
97                   &localAuthHandle) != PAM_SUCCESS)
98     {
99         return false;
100     }
101     int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
102 
103     if (retval != PAM_SUCCESS)
104     {
105         pam_end(localAuthHandle, PAM_SUCCESS);
106         return false;
107     }
108 
109     if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
110     {
111         return false;
112     }
113 
114     return true;
115 }
116