1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-2008 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2013 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Bareos authentication. Provides authentication with File and Storage daemons.
25  *
26  * Nicolas Boichat, August MMIV
27  */
28 
29 #include "monitoritem.h"
30 #include "authenticate.h"
31 #include "include/jcr.h"
32 #include "monitoritemthread.h"
33 
34 #include "lib/tls_conf.h"
35 #include "lib/bsock.h"
36 #include "lib/bnet.h"
37 #include "lib/qualified_resource_name_type_converter.h"
38 #include "lib/bstringlist.h"
39 #include "lib/parse_conf.h"
40 #include "lib/util.h"
41 
42 const int debuglevel = 50;
43 
44 /* Commands sent to Storage daemon and File daemon and received
45  *  from the User Agent */
46 static char SDFDhello[] = "Hello Director %s calling\n";
47 
48 /* Response from SD */
49 static char SDOKhello[] = "3000 OK Hello\n";
50 /* Response from FD */
51 static char FDOKhello[] = "2000 OK Hello";
52 
53 static std::map<AuthenticationResult, std::string>
54     authentication_error_to_string_map{
55         {AuthenticationResult::kNoError, "No Error"},
56         {AuthenticationResult::kAlreadyAuthenticated, "Already authenticated"},
57         {AuthenticationResult::kQualifiedResourceNameFailed,
58          "Could not generate a qualified resource name"},
59         {AuthenticationResult::kTlsHandshakeFailed, "TLS handshake failed"},
60         {AuthenticationResult::kSendHelloMessageFailed,
61          "Send of hello handshake message failed"},
62         {AuthenticationResult::kCramMd5HandshakeFailed,
63          "Challenge response handshake failed"},
64         {AuthenticationResult::kDaemonResponseFailed,
65          "Daemon response could not be read"},
66         {AuthenticationResult::kRejectedByDaemon,
67          "Authentication was rejected by the daemon"},
68         {AuthenticationResult::kUnknownDaemon, "Unkown daemon type"}};
69 
GetAuthenticationResultString(AuthenticationResult err,std::string & buffer)70 bool GetAuthenticationResultString(AuthenticationResult err,
71                                    std::string& buffer)
72 {
73   if (authentication_error_to_string_map.find(err) !=
74       authentication_error_to_string_map.end()) {
75     buffer = authentication_error_to_string_map.at(err);
76     return true;
77   }
78   return false;
79 }
80 
AuthenticateWithDirector(JobControlRecord * jcr,DirectorResource * dir_res)81 static AuthenticationResult AuthenticateWithDirector(JobControlRecord* jcr,
82                                                      DirectorResource* dir_res)
83 {
84   if (jcr->authenticated) {
85     return AuthenticationResult::kAlreadyAuthenticated;
86   }
87 
88   BareosSocket* dir = jcr->dir_bsock;
89   MonitorResource* monitor = MonitorItemThread::instance()->getMonitor();
90   if (dir_res->IsTlsConfigured()) {
91     std::string qualified_resource_name;
92     if (!my_config->GetQualifiedResourceNameTypeConverter()->ResourceToString(
93             monitor->resource_name_, R_CONSOLE, qualified_resource_name)) {
94       return AuthenticationResult::kQualifiedResourceNameFailed;
95     }
96 
97     if (!dir->DoTlsHandshake(TlsPolicy::kBnetTlsAuto, dir_res, false,
98                              qualified_resource_name.c_str(),
99                              monitor->password.value, jcr)) {
100       return AuthenticationResult::kTlsHandshakeFailed;
101     }
102   }
103 
104   uint32_t response_id;
105   BStringList response_args;
106   if (!dir->ConsoleAuthenticateWithDirector(
107           jcr, monitor->resource_name_, monitor->password, dir_res,
108           my_config->CreateOwnQualifiedNameForNetworkDump(), response_args,
109           response_id)) {
110     Jmsg(jcr, M_FATAL, 0,
111          _("Director authorization problem.\n"
112            "Most likely the passwords do not agree.\n"));
113     return AuthenticationResult::kCramMd5HandshakeFailed;
114   }
115 
116   return AuthenticationResult::kNoError;
117 }
118 
AuthenticateWithStorageDaemon(JobControlRecord * jcr,StorageResource * store)119 static AuthenticationResult AuthenticateWithStorageDaemon(
120     JobControlRecord* jcr,
121     StorageResource* store)
122 {
123   if (jcr->authenticated) {
124     return AuthenticationResult::kAlreadyAuthenticated;
125   }
126 
127   BareosSocket* sd = jcr->store_bsock;
128   MonitorResource* monitor = MonitorItemThread::instance()->getMonitor();
129   if (store->IsTlsConfigured()) {
130     std::string qualified_resource_name;
131     if (!my_config->GetQualifiedResourceNameTypeConverter()->ResourceToString(
132             monitor->resource_name_, R_DIRECTOR, qualified_resource_name)) {
133       return AuthenticationResult::kQualifiedResourceNameFailed;
134     }
135 
136     if (!sd->DoTlsHandshake(TlsPolicy::kBnetTlsAuto, store, false,
137                             qualified_resource_name.c_str(),
138                             store->password.value, jcr)) {
139       return AuthenticationResult::kTlsHandshakeFailed;
140     }
141   }
142 
143   /**
144    * Send my name to the Storage daemon then do authentication
145    */
146   char dirname[MAX_NAME_LENGTH];
147   bstrncpy(dirname, monitor->resource_name_, sizeof(dirname));
148   BashSpaces(dirname);
149 
150   sd->InitBnetDump(
151       my_config->CreateOwnQualifiedNameForNetworkDump());
152 
153   if (!sd->fsend(SDFDhello, dirname)) {
154     Dmsg1(debuglevel, _("Error sending Hello to Storage daemon. ERR=%s\n"),
155           BnetStrerror(sd));
156     Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to Storage daemon. ERR=%s\n"),
157          BnetStrerror(sd));
158     return AuthenticationResult::kSendHelloMessageFailed;
159   }
160 
161   bool auth_success = sd->AuthenticateOutboundConnection(
162       jcr, my_config->CreateOwnQualifiedNameForNetworkDump(), dirname,
163       store->password, store);
164   if (!auth_success) {
165     Dmsg2(debuglevel,
166           "Director unable to authenticate with Storage daemon at \"%s:%d\"\n",
167           sd->host(), sd->port());
168     Jmsg(jcr, M_FATAL, 0,
169          _("Director unable to authenticate with Storage daemon at \"%s:%d\". "
170            "Possible causes:\n"
171            "Passwords or names not the same or\n"
172            "TLS negotiation problem or\n"
173            "Maximum Concurrent Jobs exceeded on the SD or\n"
174            "SD networking messed up (restart daemon).\n"),
175          sd->host(), sd->port());
176     return AuthenticationResult::kCramMd5HandshakeFailed;
177   }
178 
179   Dmsg1(116, ">stored: %s", sd->msg);
180   if (sd->recv() <= 0) {
181     Jmsg3(jcr, M_FATAL, 0,
182           _("dir<stored: \"%s:%s\" bad response to Hello command: ERR=%s\n"),
183           sd->who(), sd->host(), sd->bstrerror());
184     return AuthenticationResult::kDaemonResponseFailed;
185   }
186 
187   Dmsg1(110, "<stored: %s", sd->msg);
188   if (!bstrncmp(sd->msg, SDOKhello, sizeof(SDOKhello))) {
189     Dmsg0(debuglevel, _("Storage daemon rejected Hello command\n"));
190     Jmsg2(jcr, M_FATAL, 0,
191           _("Storage daemon at \"%s:%d\" rejected Hello command\n"), sd->host(),
192           sd->port());
193     return AuthenticationResult::kRejectedByDaemon;
194   }
195 
196   return AuthenticationResult::kNoError;
197 }
198 
AuthenticateWithFileDaemon(JobControlRecord * jcr,ClientResource * client)199 static AuthenticationResult AuthenticateWithFileDaemon(JobControlRecord* jcr,
200                                                        ClientResource* client)
201 {
202   if (jcr->authenticated) {
203     return AuthenticationResult::kAlreadyAuthenticated;
204   }
205 
206   BareosSocket* fd = jcr->file_bsock;
207   MonitorResource* monitor = MonitorItemThread::instance()->getMonitor();
208   if (client->IsTlsConfigured()) {
209     std::string qualified_resource_name;
210     if (!my_config->GetQualifiedResourceNameTypeConverter()->ResourceToString(
211             monitor->resource_name_, R_DIRECTOR, qualified_resource_name)) {
212       return AuthenticationResult::kQualifiedResourceNameFailed;
213     }
214 
215     if (!fd->DoTlsHandshake(TlsPolicy::kBnetTlsAuto, client, false,
216                             qualified_resource_name.c_str(),
217                             client->password.value, jcr)) {
218       return AuthenticationResult::kTlsHandshakeFailed;
219     }
220   }
221 
222   /**
223    * Send my name to the File daemon then do authentication
224    */
225   char dirname[MAX_NAME_LENGTH];
226   bstrncpy(dirname, monitor->resource_name_, sizeof(dirname));
227   BashSpaces(dirname);
228 
229   fd->InitBnetDump(
230       my_config->CreateOwnQualifiedNameForNetworkDump());
231 
232   if (!fd->fsend(SDFDhello, dirname)) {
233     Jmsg(jcr, M_FATAL, 0,
234          _("Error sending Hello to File daemon at \"%s:%d\". ERR=%s\n"),
235          fd->host(), fd->port(), fd->bstrerror());
236     return AuthenticationResult::kSendHelloMessageFailed;
237   }
238   Dmsg1(debuglevel, "Sent: %s", fd->msg);
239 
240   bool auth_success = fd->AuthenticateOutboundConnection(
241       jcr, my_config->CreateOwnQualifiedNameForNetworkDump(), dirname,
242       client->password, client);
243 
244   if (!auth_success) {
245     Dmsg2(debuglevel, "Unable to authenticate with File daemon at \"%s:%d\"\n",
246           fd->host(), fd->port());
247     Jmsg(jcr, M_FATAL, 0,
248          _("Unable to authenticate with File daemon at \"%s:%d\". Possible "
249            "causes:\n"
250            "Passwords or names not the same or\n"
251            "TLS negotiation failed or\n"
252            "Maximum Concurrent Jobs exceeded on the FD or\n"
253            "FD networking messed up (restart daemon).\n"),
254          fd->host(), fd->port());
255     return AuthenticationResult::kCramMd5HandshakeFailed;
256   }
257 
258   Dmsg1(116, ">filed: %s", fd->msg);
259   if (fd->recv() <= 0) {
260     Dmsg1(debuglevel,
261           _("Bad response from File daemon to Hello command: ERR=%s\n"),
262           BnetStrerror(fd));
263     Jmsg(jcr, M_FATAL, 0,
264          _("Bad response from File daemon at \"%s:%d\" to Hello command: "
265            "ERR=%s\n"),
266          fd->host(), fd->port(), fd->bstrerror());
267     return AuthenticationResult::kDaemonResponseFailed;
268   }
269 
270   Dmsg1(110, "<filed: %s", fd->msg);
271   if (strncmp(fd->msg, FDOKhello, sizeof(FDOKhello) - 1) != 0) {
272     Jmsg(jcr, M_FATAL, 0, _("File daemon rejected Hello command\n"));
273     return AuthenticationResult::kRejectedByDaemon;
274   }
275 
276   return AuthenticationResult::kNoError;
277 }
278 
AuthenticateWithDaemon(MonitorItem * item,JobControlRecord * jcr)279 AuthenticationResult AuthenticateWithDaemon(MonitorItem* item,
280                                             JobControlRecord* jcr)
281 {
282   switch (item->type()) {
283     case R_DIRECTOR:
284       return AuthenticateWithDirector(jcr, (DirectorResource*)item->resource());
285     case R_CLIENT:
286       return AuthenticateWithFileDaemon(jcr, (ClientResource*)item->resource());
287     case R_STORAGE:
288       return AuthenticateWithStorageDaemon(jcr,
289                                            (StorageResource*)item->resource());
290     default:
291       printf(_("Error, currentitem is not a Client or a Storage..\n"));
292       return AuthenticationResult::kUnknownDaemon;
293   }
294 }
295