1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2018-2020 Bareos GmbH & Co. KG
5 
6    This program is Free Software; you can redistribute it and/or
7    modify it under the terms of version three of the GNU Affero General Public
8    License as published by the Free Software Foundation and included
9    in the file LICENSE.
10 
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14    Affero General Public License for more details.
15 
16    You should have received a copy of the GNU Affero General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA.
20 */
21 
22 #include "auth_pam.h"
23 
24 #include <cstring>
25 #include <security/pam_appl.h>
26 
27 #include "include/bareos.h"
28 #include "dird/ua.h"
29 
30 static const int debuglevel = 200;
31 
32 static const std::string service_name("bareos");
33 
34 struct PamData {
35   BareosSocket* UA_sock_;
36   const std::string& passwd_;
37 
PamDataPamData38   PamData(BareosSocket* UA_sock, const std::string& passwd)
39       : UA_sock_(UA_sock), passwd_(passwd)
40   {
41   }
42 };
43 
PamConvSendMessage(BareosSocket * UA_sock,const char * msg,int msg_style)44 static bool PamConvSendMessage(BareosSocket* UA_sock,
45                                const char* msg,
46                                int msg_style)
47 {
48   char buf = msg_style;
49   if (!UA_sock->send((const char*)&buf, 1)) {
50     Dmsg0(debuglevel, "PamConvSendMessage error\n");
51     return false;
52   }
53   if (!UA_sock->send(msg, strlen(msg) + 1)) {
54     Dmsg0(debuglevel, "PamConvSendMessage error\n");
55     return false;
56   }
57   return true;
58 }
59 
PamConversationCallback(int num_msg,struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)60 static int PamConversationCallback(int num_msg,
61 #if defined(__sun)
62                                    struct pam_message** msgm,
63 #else
64                                    const struct pam_message** msgm,
65 #endif
66                                    struct pam_response** response,
67                                    void* appdata_ptr)
68 {
69   if (!appdata_ptr) {
70     Dmsg0(debuglevel, "pam_conv_callback pointer error\n");
71     return PAM_BUF_ERR;
72   }
73 
74   if ((num_msg <= 0) || (num_msg > PAM_MAX_NUM_MSG)) {
75     Dmsg0(debuglevel, "pam_conv_callback wrong number of messages\n");
76     return (PAM_CONV_ERR);
77   }
78 
79   struct pam_response* resp =
80       static_cast<pam_response*>(calloc(num_msg, sizeof(struct pam_response)));
81 
82   if (!resp) {
83     Dmsg0(debuglevel, "pam_conv_callback memory error\n");
84     return PAM_BUF_ERR;
85   }
86 
87   PamData* pam_data = static_cast<PamData*>(appdata_ptr);
88 
89   bool error = false;
90   int i = 0;
91   for (; i < num_msg && !error; i++) {
92     switch (msgm[i]->msg_style) {
93       case PAM_PROMPT_ECHO_OFF:
94       case PAM_PROMPT_ECHO_ON:
95         if (!PamConvSendMessage(pam_data->UA_sock_, msgm[i]->msg,
96                                 msgm[i]->msg_style)) {
97           error = true;
98           break;
99         }
100         if (pam_data->UA_sock_->IsStop() || pam_data->UA_sock_->IsError()) {
101           error = true;
102           break;
103         }
104         if (pam_data->UA_sock_->recv()) {
105           resp[i].resp = strdup(pam_data->UA_sock_->msg);
106           resp[i].resp_retcode = 0;
107         }
108         if (pam_data->UA_sock_->IsStop() || pam_data->UA_sock_->IsError()) {
109           error = true;
110           break;
111         }
112         break;
113       case PAM_ERROR_MSG:
114       case PAM_TEXT_INFO:
115         if (!PamConvSendMessage(pam_data->UA_sock_, msgm[i]->msg,
116                                 PAM_PROMPT_ECHO_ON)) {
117           error = true;
118         }
119         break;
120       default:
121         Dmsg3(debuglevel, "message[%d]: pam error type: %d error: \"%s\"\n", 1,
122               msgm[i]->msg_style, msgm[i]->msg);
123         error = true;
124         break;
125     } /* switch (msgm[i]->msg_style) { */
126   }   /* for( ; i < num_msg ..) */
127 
128   if (error) {
129     for (int i = 0; i < num_msg; ++i) {
130       if (resp[i].resp) {
131         memset(resp[i].resp, 0, strlen(resp[i].resp));
132         free(resp[i].resp);
133       }
134     }
135     memset(resp, 0, num_msg * sizeof *resp);
136     free(resp);
137     *response = nullptr;
138     return PAM_CONV_ERR;
139   }
140 
141   *response = resp;
142   return PAM_SUCCESS;
143 }
144 
PamLocalCallback(int num_msg,struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)145 static int PamLocalCallback(int num_msg,
146 #if defined(__sun)
147                             struct pam_message** msgm,
148 #else
149                             const struct pam_message** msgm,
150 #endif
151                             struct pam_response** response,
152                             void* appdata_ptr)
153 {
154   struct pam_response* resp =
155       static_cast<pam_response*>(calloc(num_msg, sizeof(struct pam_response)));
156 
157   PamData* pam_data = static_cast<PamData*>(appdata_ptr);
158 
159   if (num_msg == 1) {
160     resp[0].resp = strdup(pam_data->passwd_.c_str());
161     resp[0].resp_retcode = 0;
162   }
163 
164   *response = resp;
165   return PAM_SUCCESS;
166 }
167 
PamAuthenticateUser(BareosSocket * UA_sock,const std::string & username_in,const std::string & password_in,std::string & authenticated_username)168 bool PamAuthenticateUser(BareosSocket* UA_sock,
169                          const std::string& username_in,
170                          const std::string& password_in,
171                          std::string& authenticated_username)
172 {
173   std::unique_ptr<PamData> pam_callback_data(new PamData(UA_sock, password_in));
174   std::unique_ptr<struct pam_conv> pam_conversation_container(
175       new struct pam_conv);
176   struct pam_handle* pamh = nullptr; /* pam session handle */
177 
178   bool interactive = true;
179   if (!username_in.empty() && !password_in.empty()) { interactive = false; }
180   pam_conversation_container->conv =
181       interactive ? PamConversationCallback : PamLocalCallback;
182   pam_conversation_container->appdata_ptr = pam_callback_data.get();
183 
184   const char* username = username_in.empty() ? nullptr : username_in.c_str();
185   int err = pam_start(service_name.c_str(), username,
186                       pam_conversation_container.get(), &pamh);
187   if (err != PAM_SUCCESS) {
188     Dmsg1(debuglevel, "PAM start failed: %s\n", pam_strerror(pamh, err));
189     return false;
190   }
191 
192   err = pam_set_item(pamh, PAM_RUSER, username);
193   if (err != PAM_SUCCESS) {
194     Dmsg1(debuglevel, "PAM set_item failed: %s\n", pam_strerror(pamh, err));
195     return false;
196   }
197 
198   err = pam_authenticate(pamh, 0);
199   if (err != PAM_SUCCESS) {
200     Dmsg1(debuglevel, "PAM authentication failed: %s\n",
201           pam_strerror(pamh, err));
202     return false;
203   }
204 
205 #if defined(__sun)
206   void* data;
207 #else
208   const void* data;
209 #endif
210   err = pam_get_item(pamh, PAM_USER, &data);
211   if (err != PAM_SUCCESS) {
212     Dmsg1(debuglevel, "PAM get_item failed: %s\n", pam_strerror(pamh, err));
213     return false;
214   } else {
215     if (data) {
216       const char* temp_str = static_cast<const char*>(data);
217       authenticated_username = temp_str;
218     }
219   }
220 
221   if (pam_end(pamh, err) != PAM_SUCCESS) {
222     Dmsg1(debuglevel, "PAM end failed: %s\n", pam_strerror(pamh, err));
223     return false;
224   }
225 
226   if (err == PAM_SUCCESS) {
227     bool ok = true;
228     if (interactive) { ok = PamConvSendMessage(UA_sock, "", PAM_SUCCESS); }
229     return ok;
230   }
231   return false;
232 }
233