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