1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2018-2018 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)
40     , passwd_(passwd) { }
41 };
42 
PamConvSendMessage(BareosSocket * UA_sock,const char * msg,int msg_style)43 static bool PamConvSendMessage(BareosSocket *UA_sock, const char *msg, int msg_style)
44 {
45    char buf = msg_style;
46    if (!UA_sock->send((const char*)&buf, 1)) {
47       Dmsg0(debuglevel, "PamConvSendMessage error\n");
48       return false;
49    }
50    if (!UA_sock->send(msg, strlen(msg) +1)) {
51       Dmsg0(debuglevel, "PamConvSendMessage error\n");
52       return false;
53    }
54    return true;
55 }
56 
PamConversationCallback(int num_msg,const struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)57 static int PamConversationCallback(int num_msg, const struct pam_message **msgm,
58                 struct pam_response **response, void *appdata_ptr)
59 {
60    if (!appdata_ptr) {
61       Dmsg0(debuglevel, "pam_conv_callback pointer error\n");
62       return PAM_BUF_ERR;
63    }
64 
65    if ((num_msg <= 0) || (num_msg > PAM_MAX_NUM_MSG)) {
66       Dmsg0(debuglevel, "pam_conv_callback wrong number of messages\n");
67       return (PAM_CONV_ERR);
68    }
69 
70    struct pam_response *resp =
71          reinterpret_cast<pam_response *> (actuallycalloc(
72                num_msg, sizeof(struct pam_response)));
73 
74    if (!resp) {
75       Dmsg0(debuglevel, "pam_conv_callback memory error\n");
76       return PAM_BUF_ERR;
77    }
78 
79    PamData *pam_data = reinterpret_cast<PamData *>(appdata_ptr);
80 
81    bool error = false;
82    int i = 0;
83    for ( ; i < num_msg && !error; i++) {
84       switch (msgm[i]->msg_style) {
85          case PAM_PROMPT_ECHO_OFF:
86          case PAM_PROMPT_ECHO_ON:
87             if (!PamConvSendMessage(pam_data->UA_sock_,
88                    msgm[i]->msg, msgm[i]->msg_style)) {
89                error = true;
90                break;
91             }
92             if (pam_data->UA_sock_->IsStop() || pam_data->UA_sock_->IsError()) {
93                error = true;
94                break;
95             }
96             if (pam_data->UA_sock_->recv()) {
97                resp[i].resp = actuallystrdup(pam_data->UA_sock_->msg);
98                resp[i].resp_retcode = 0;
99             }
100             if (pam_data->UA_sock_->IsStop() || pam_data->UA_sock_->IsError()) {
101                error = true;
102                break;
103             }
104             break;
105          case PAM_ERROR_MSG:
106          case PAM_TEXT_INFO:
107             if (!PamConvSendMessage(pam_data->UA_sock_,
108                   msgm[i]->msg, PAM_PROMPT_ECHO_ON)) {
109                error = true;
110             }
111             break;
112          default:
113             Dmsg3(debuglevel, "message[%d]: pam error type: %d error: \"%s\"\n",
114                   1, msgm[i]->msg_style, msgm[i]->msg);
115             error = true;
116             break;
117       } /* switch (msgm[i]->msg_style) { */
118    } /* for( ; i < num_msg ..) */
119 
120    if (error) {
121       for (int i = 0; i < num_msg; ++i) {
122          if (resp[i].resp) {
123             memset(resp[i].resp, 0, strlen(resp[i].resp));
124             Actuallyfree(resp[i].resp);
125          }
126       }
127       memset(resp, 0, num_msg * sizeof *resp);
128       Actuallyfree(resp);
129       *response = nullptr;
130       return PAM_CONV_ERR;
131    }
132 
133    *response = resp;
134    return PAM_SUCCESS;
135 }
136 
PamLocalCallback(int num_msg,const struct pam_message ** msgm,struct pam_response ** response,void * appdata_ptr)137 static int PamLocalCallback(int num_msg, const struct pam_message **msgm,
138                 struct pam_response **response, void *appdata_ptr)
139 {
140    struct pam_response *resp =
141          reinterpret_cast<pam_response *> (actuallycalloc(
142                num_msg, sizeof(struct pam_response)));
143 
144    PamData *pam_data = reinterpret_cast<PamData *>(appdata_ptr);
145 
146    if (num_msg == 1) {
147      resp[0].resp = actuallystrdup(pam_data->passwd_.c_str());
148      resp[0].resp_retcode = 0;
149    }
150 
151    *response = resp;
152    return PAM_SUCCESS;
153 }
154 
PamAuthenticateUser(BareosSocket * UA_sock,const std::string & username_in,const std::string & password_in,std::string & authenticated_username)155 bool PamAuthenticateUser(BareosSocket *UA_sock,
156                          const std::string &username_in,
157                          const std::string &password_in,
158                          std::string& authenticated_username)
159 {
160    std::unique_ptr<PamData> pam_callback_data(new PamData(UA_sock, password_in));
161    std::unique_ptr<struct pam_conv> pam_conversation_container(new struct pam_conv);
162    struct pam_handle *pamh = nullptr; /* pam session handle */
163 
164    bool interactive = true;
165    if(!username_in.empty() && !password_in.empty()) {
166      interactive = false;
167    }
168    pam_conversation_container->conv = interactive ? PamConversationCallback : PamLocalCallback;
169    pam_conversation_container->appdata_ptr = pam_callback_data.get();
170 
171    const char *username = username_in.empty() ? nullptr : username_in.c_str();
172    int err = pam_start(service_name.c_str(), username,
173                       pam_conversation_container.get(), &pamh);
174    if (err != PAM_SUCCESS) {
175       Dmsg1(debuglevel, "PAM start failed: %s\n", pam_strerror(pamh, err));
176       return false;
177    }
178 
179    err = pam_set_item(pamh, PAM_RUSER, username);
180    if (err != PAM_SUCCESS) {
181       Dmsg1(debuglevel, "PAM set_item failed: %s\n", pam_strerror(pamh, err));
182       return false;
183    }
184 
185    err = pam_authenticate(pamh, 0);
186    if (err != PAM_SUCCESS) {
187       Dmsg1(debuglevel, "PAM authentication failed: %s\n", pam_strerror(pamh, err));
188       return false;
189    }
190 
191    const void* data;
192    err = pam_get_item(pamh, PAM_USER, &data);
193    if (err != PAM_SUCCESS) {
194       Dmsg1(debuglevel, "PAM get_item failed: %s\n", pam_strerror(pamh, err));
195       return false;
196    } else {
197      if (data) {
198        const char *temp_str = reinterpret_cast<const char*>(data);
199        authenticated_username = temp_str;
200      }
201    }
202 
203    if (pam_end(pamh, err) != PAM_SUCCESS) {
204       Dmsg1(debuglevel, "PAM end failed: %s\n", pam_strerror(pamh, err));
205       return false;
206    }
207 
208    if (err == PAM_SUCCESS) {
209       bool ok = true;
210       if (interactive) {
211          ok = PamConvSendMessage(UA_sock, "", PAM_SUCCESS);
212       }
213       return ok;
214    }
215    return false;
216 }
217