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