1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2018 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 * Kern Sibbald, January MM, September MM
25 */
26 /**
27 * @file
28 * Main configuration file parser for Bareos User Agent
29 * some parts may be split into separate files such as
30 * the schedule configuration (sch_config.c).
31 *
32 * Note, the configuration file parser consists of three parts
33 *
34 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
35 *
36 * 2. The generic config scanner in lib/parse_config.c and
37 * lib/parse_config.h. These files contain the parser code,
38 * some utility routines, and the common store routines
39 * (name, int, string).
40 *
41 * 3. The daemon specific file, which contains the Resource
42 * definitions as well as any specific store routines
43 * for the resource records.
44 */
45
46 #define NEED_JANSSON_NAMESPACE 1
47 #include "include/bareos.h"
48 #include "console/console_globals.h"
49 #include "console/console_conf.h"
50 #include "lib/json.h"
51
52 namespace console {
53
54 /**
55 * Define the first and last resource ID record
56 * types. Note, these should be unique for each
57 * daemon though not a requirement.
58 */
59 static CommonResourceHeader *sres_head[R_LAST - R_FIRST + 1];
60 static CommonResourceHeader **res_head = sres_head;
61
62 /* Forward referenced subroutines */
63 static bool SaveResource(int type, ResourceItem *items, int pass);
64 static void FreeResource(CommonResourceHeader *sres, int type);
65 static void DumpResource(int type,
66 CommonResourceHeader *reshdr,
67 void sendit(void *sock, const char *fmt, ...),
68 void *sock,
69 bool hide_sensitive_data,
70 bool verbose);
71
72 /* We build the current resource here as we are
73 * scanning the resource configuration definition,
74 * then move it to allocated memory when the resource
75 * scan is complete.
76 */
77 static UnionOfResources res_all;
78 static int32_t res_all_size = sizeof(res_all);
79
80 /* Definition of records permitted within each
81 * resource with the routine to process the record
82 * information.
83 */
84
85 /* Console "globals" */
86 static ResourceItem cons_items[] = {
87 { "Name", CFG_TYPE_NAME, ITEM(res_cons.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL, "The name of this resource." },
88 { "Description", CFG_TYPE_STR, ITEM(res_cons.hdr.desc), 0, 0, NULL, NULL, NULL },
89 { "RcFile", CFG_TYPE_DIR, ITEM(res_cons.rc_file), 0, 0, NULL, NULL, NULL },
90 { "HistoryFile", CFG_TYPE_DIR, ITEM(res_cons.history_file), 0, 0, NULL, NULL, NULL },
91 { "HistoryLength", CFG_TYPE_PINT32, ITEM(res_cons.history_length), 0, CFG_ITEM_DEFAULT, "100", NULL, NULL },
92 { "Password", CFG_TYPE_MD5PASSWORD, ITEM(res_cons.password), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
93 { "Director", CFG_TYPE_STR, ITEM(res_cons.director), 0, 0, NULL, NULL, NULL },
94 { "HeartbeatInterval", CFG_TYPE_TIME, ITEM(res_cons.heartbeat_interval), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
95 TLS_COMMON_CONFIG(res_dir),
96 TLS_CERT_CONFIG(res_dir),
97 { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
98 };
99
100 /* Director's that we can contact */
101 static ResourceItem dir_items[] = {
102 { "Name", CFG_TYPE_NAME, ITEM(res_dir.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
103 { "Description", CFG_TYPE_STR, ITEM(res_dir.hdr.desc), 0, 0, NULL, NULL, NULL },
104 { "DirPort", CFG_TYPE_PINT32, ITEM(res_dir.DIRport), 0, CFG_ITEM_DEFAULT, DIR_DEFAULT_PORT, NULL, NULL },
105 { "Address", CFG_TYPE_STR, ITEM(res_dir.address), 0, 0, NULL, NULL, NULL },
106 { "Password", CFG_TYPE_MD5PASSWORD, ITEM(res_dir.password_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
107 { "HeartbeatInterval", CFG_TYPE_TIME, ITEM(res_dir.heartbeat_interval), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
108 TLS_COMMON_CONFIG(res_dir),
109 TLS_CERT_CONFIG(res_dir),
110 { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
111 };
112
113 /**
114 * This is the master resource definition.
115 * It must have one item for each of the resources.
116 */
117 static ResourceTable resources[] = {
__anonb6768caf0102()118 { "Console", cons_items, R_CONSOLE, sizeof(ConsoleResource), [] (void *res){ return new((ConsoleResource *) res) ConsoleResource(); } },
__anonb6768caf0202()119 { "Director", dir_items, R_DIRECTOR, sizeof(DirectorResource), [] (void *res){ return new((DirectorResource *) res) DirectorResource(); } },
120 { NULL, NULL, 0 }
121 };
122
123
DumpResource(int type,CommonResourceHeader * reshdr,void sendit (void * sock,const char * fmt,...),void * sock,bool hide_sensitive_data,bool verbose)124 static void DumpResource(int type,
125 CommonResourceHeader *reshdr,
126 void sendit(void *sock, const char *fmt, ...),
127 void *sock,
128 bool hide_sensitive_data,
129 bool verbose)
130 {
131 PoolMem buf;
132 UnionOfResources *res = (UnionOfResources *)reshdr;
133 BareosResource *resclass;
134 bool recurse = true;
135
136 if (res == NULL) {
137 sendit(sock, _("Warning: no \"%s\" resource (%d) defined.\n"), my_config->res_to_str(type), type);
138 return;
139 }
140 if (type < 0) { /* no recursion */
141 type = - type;
142 recurse = false;
143 }
144
145 switch (type) {
146 default:
147 resclass = (BareosResource *)reshdr;
148 resclass->PrintConfig(buf, *my_config);
149 break;
150 }
151 sendit(sock, "%s", buf.c_str());
152
153 if (recurse && res->res_dir.hdr.next) {
154 my_config->DumpResourceCb_(type, res->res_dir.hdr.next, sendit, sock, hide_sensitive_data, verbose);
155 }
156 }
157
158 /**
159 * Free memory of resource.
160 * NB, we don't need to worry about freeing any references
161 * to other resources as they will be freed when that
162 * resource chain is traversed. Mainly we worry about freeing
163 * allocated strings (names).
164 */
FreeResource(CommonResourceHeader * sres,int type)165 static void FreeResource(CommonResourceHeader *sres, int type)
166 {
167 CommonResourceHeader *nres;
168 UnionOfResources *res = (UnionOfResources *)sres;
169
170 if (res == NULL) return;
171
172 /* common stuff -- free the resource name */
173 nres = (CommonResourceHeader *)res->res_dir.hdr.next;
174 if (res->res_dir.hdr.name) { free(res->res_dir.hdr.name); }
175 if (res->res_dir.hdr.desc) { free(res->res_dir.hdr.desc); }
176
177 switch (type) {
178 case R_CONSOLE:
179 if (res->res_cons.rc_file) { free(res->res_cons.rc_file); }
180 if (res->res_cons.history_file) { free(res->res_cons.history_file); }
181 if (res->res_cons.tls_cert_.allowed_certificate_common_names_) {
182 res->res_cons.tls_cert_.allowed_certificate_common_names_->destroy();
183 free(res->res_cons.tls_cert_.allowed_certificate_common_names_);
184 }
185 if (res->res_cons.tls_cert_.ca_certfile_) { delete res->res_cons.tls_cert_.ca_certfile_; }
186 if (res->res_cons.tls_cert_.ca_certdir_) { delete res->res_cons.tls_cert_.ca_certdir_; }
187 if (res->res_cons.tls_cert_.crlfile_) { delete res->res_cons.tls_cert_.crlfile_; }
188 if (res->res_cons.tls_cert_.certfile_) { delete res->res_cons.tls_cert_.certfile_; }
189 if (res->res_cons.tls_cert_.keyfile_) { delete res->res_cons.tls_cert_.keyfile_; }
190 if (res->res_cons.cipherlist_) { delete res->res_cons.cipherlist_; }
191 if (res->res_cons.tls_cert_.dhfile_) { delete res->res_cons.tls_cert_.dhfile_; }
192 if (res->res_cons.tls_cert_.pem_message_) { delete res->res_cons.tls_cert_.pem_message_; }
193 break;
194 case R_DIRECTOR:
195 if (res->res_dir.address) { free(res->res_dir.address); }
196 if (res->res_dir.tls_cert_.allowed_certificate_common_names_) {
197 res->res_dir.tls_cert_.allowed_certificate_common_names_->destroy();
198 free(res->res_dir.tls_cert_.allowed_certificate_common_names_);
199 }
200 if (res->res_dir.tls_cert_.ca_certfile_) { delete res->res_dir.tls_cert_.ca_certfile_; }
201 if (res->res_dir.tls_cert_.ca_certdir_) { delete res->res_dir.tls_cert_.ca_certdir_; }
202 if (res->res_dir.tls_cert_.crlfile_) { delete res->res_dir.tls_cert_.crlfile_; }
203 if (res->res_dir.tls_cert_.certfile_) { delete res->res_dir.tls_cert_.certfile_; }
204 if (res->res_dir.tls_cert_.keyfile_) { delete res->res_dir.tls_cert_.keyfile_; }
205 if (res->res_dir.cipherlist_) { delete res->res_dir.cipherlist_; }
206 if (res->res_dir.tls_cert_.dhfile_) { delete res->res_dir.tls_cert_.dhfile_; }
207 if (res->res_dir.tls_cert_.pem_message_) { delete res->res_dir.tls_cert_.pem_message_; }
208 break;
209 default:
210 printf(_("Unknown resource type %d\n"), type);
211 }
212 /* Common stuff again -- free the resource, recurse to next one */
213 free(res);
214 if (nres) { my_config->FreeResourceCb_(nres, type); }
215 }
216
217 /**
218 * Save the new resource by chaining it into the head list for
219 * the resource. If this is pass 2, we update any resource
220 * pointers (currently only in the Job resource).
221 */
SaveResource(int type,ResourceItem * items,int pass)222 static bool SaveResource(int type, ResourceItem *items, int pass)
223 {
224 UnionOfResources *res;
225 int rindex = type - R_FIRST;
226 int i;
227 int error = 0;
228
229 /*
230 * Ensure that all required items are present
231 */
232 for (i = 0; items[i].name; i++) {
233 if (items[i].flags & CFG_ITEM_REQUIRED) {
234 if (!BitIsSet(i, res_all.res_dir.hdr.item_present)) {
235 Emsg2(M_ABORT, 0, _("%s item is required in %s resource, but not found.\n"),
236 items[i].name, resources[rindex].name);
237 }
238 }
239 }
240
241 /*
242 * During pass 2, we looked up pointers to all the resources
243 * referrenced in the current resource, , now we
244 * must copy their address from the static record to the allocated
245 * record.
246 */
247 if (pass == 2) {
248 switch (type) {
249 case R_CONSOLE:
250 if ((res = (UnionOfResources *)my_config->GetResWithName(R_CONSOLE, res_all.res_cons.name())) == NULL) {
251 Emsg1(M_ABORT, 0, _("Cannot find Console resource %s\n"), res_all.res_cons.name());
252 } else {
253 res->res_cons.tls_cert_.allowed_certificate_common_names_ = res_all.res_cons.tls_cert_.allowed_certificate_common_names_;
254 }
255 break;
256 case R_DIRECTOR:
257 if ((res = (UnionOfResources *)my_config->GetResWithName(R_DIRECTOR, res_all.res_dir.name())) == NULL) {
258 Emsg1(M_ABORT, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.name());
259 } else {
260 res->res_dir.tls_cert_.allowed_certificate_common_names_ = res_all.res_dir.tls_cert_.allowed_certificate_common_names_;
261 }
262 break;
263 default:
264 Emsg1(M_ERROR, 0, _("Unknown resource type %d\n"), type);
265 error = 1;
266 break;
267 }
268
269 /*
270 * Note, the resoure name was already saved during pass 1,
271 * so here, we can just release it.
272 */
273 if (res_all.res_dir.hdr.name) {
274 free(res_all.res_dir.hdr.name);
275 res_all.res_dir.hdr.name = NULL;
276 }
277 if (res_all.res_dir.hdr.desc) {
278 free(res_all.res_dir.hdr.desc);
279 res_all.res_dir.hdr.desc = NULL;
280 }
281 return (error == 0);
282 }
283
284 /*
285 * Common
286 */
287 if (!error) {
288 res = (UnionOfResources *)malloc(resources[rindex].size);
289 memcpy(res, &res_all, resources[rindex].size);
290 if (!res_head[rindex]) {
291 res_head[rindex] = (CommonResourceHeader *)res; /* store first entry */
292 } else {
293 CommonResourceHeader *next, *last;
294 for (last=next=res_head[rindex]; next; next=next->next) {
295 last = next;
296 if (bstrcmp(next->name, res->res_dir.name())) {
297 Emsg2(M_ERROR_TERM, 0,
298 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
299 resources[rindex].name, res->res_dir.name());
300 }
301 }
302 last->next = (CommonResourceHeader *)res;
303 Dmsg2(90, "Inserting %s res: %s\n", my_config->res_to_str(type), res->res_dir.name());
304 }
305 }
306 return (error == 0);
307 }
308
ConfigReadyCallback(ConfigurationParser & my_config)309 static void ConfigReadyCallback(ConfigurationParser &my_config)
310 {
311 std::map<int, std::string> map{{R_DIRECTOR, "R_DIRECTOR"}, {R_CONSOLE, "R_CONSOLE"}};
312 my_config.InitializeQualifiedResourceNameTypeConverter(map);
313 }
314
InitConsConfig(const char * configfile,int exit_code)315 ConfigurationParser *InitConsConfig(const char *configfile, int exit_code)
316 {
317 ConfigurationParser *config =
318 new ConfigurationParser(configfile, nullptr, nullptr, nullptr, nullptr, nullptr, exit_code,
319 (void *)&res_all, res_all_size, R_FIRST, R_LAST, resources, res_head,
320 default_config_filename.c_str(), "bconsole.d", ConfigReadyCallback,
321 SaveResource, DumpResource, FreeResource);
322 if (config) { config->r_own_ = R_CONSOLE; }
323 return config;
324 }
325
326 /**
327 * Print configuration file schema in json format
328 */
329 #ifdef HAVE_JANSSON
PrintConfigSchemaJson(PoolMem & buffer)330 bool PrintConfigSchemaJson(PoolMem &buffer)
331 {
332 ResourceTable *resources = my_config->resources_;
333
334 InitializeJson();
335
336 json_t *json = json_object();
337 json_object_set_new(json, "format-version", json_integer(2));
338 json_object_set_new(json, "component", json_string("bconsole"));
339 json_object_set_new(json, "version", json_string(VERSION));
340
341 /*
342 * Resources
343 */
344 json_t *resource = json_object();
345 json_object_set(json, "resource", resource);
346 json_t *bconsole = json_object();
347 json_object_set(resource, "bconsole", bconsole);
348
349 for (int r = 0; resources[r].name; r++) {
350 ResourceTable resource = my_config->resources_[r];
351 json_object_set(bconsole, resource.name, json_items(resource.items));
352 }
353
354 PmStrcat(buffer, json_dumps(json, JSON_INDENT(2)));
355 json_decref(json);
356
357 return true;
358 }
359 #else
PrintConfigSchemaJson(PoolMem & buffer)360 bool PrintConfigSchemaJson(PoolMem &buffer)
361 {
362 PmStrcat(buffer, "{ \"success\": false, \"message\": \"not available\" }");
363 return false;
364 }
365 #endif
366 } /* namespace console */
367