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     auto* pass = reinterpret_cast<char*>(
18         malloc(std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1));
19     std::strcpy(pass, reinterpret_cast<char*>(appdataPtr));
20 
21     *resp = reinterpret_cast<pam_response*>(
22         calloc(numMsg, sizeof(struct pam_response)));
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         resp[i]->resp = pass;
34     }
35 
36     return PAM_SUCCESS;
37 }
38 
39 inline bool pamAuthenticateUser(const boost::string_view username,
40                                 const boost::string_view password)
41 {
42     std::string userStr(username);
43     std::string passStr(password);
44     const struct pam_conv localConversation = {
45         pamFunctionConversation, const_cast<char*>(passStr.c_str())};
46     pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
47 
48     if (pam_start("webserver", userStr.c_str(), &localConversation,
49                   &localAuthHandle) != PAM_SUCCESS)
50     {
51         return false;
52     }
53     int retval = pam_authenticate(localAuthHandle,
54                                   PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
55 
56     if (retval != PAM_SUCCESS)
57     {
58         pam_end(localAuthHandle, PAM_SUCCESS);
59         return false;
60     }
61 
62     /* check that the account is healthy */
63     if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) !=
64         PAM_SUCCESS)
65     {
66         pam_end(localAuthHandle, PAM_SUCCESS);
67         return false;
68     }
69 
70     if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
71     {
72         return false;
73     }
74 
75     return true;
76 }
77 
78 inline bool pamUpdatePassword(const std::string& username,
79                               const std::string& password)
80 {
81     const struct pam_conv localConversation = {
82         pamFunctionConversation, const_cast<char*>(password.c_str())};
83     pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
84 
85     if (pam_start("passwd", username.c_str(), &localConversation,
86                   &localAuthHandle) != PAM_SUCCESS)
87     {
88         return false;
89     }
90     int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
91 
92     if (retval != PAM_SUCCESS)
93     {
94         pam_end(localAuthHandle, PAM_SUCCESS);
95         return false;
96     }
97 
98     if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
99     {
100         return false;
101     }
102 
103     return true;
104 }
105