Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file extensions.c
18 : * @brief Utility functions for extensions
19 : * @author Özgür Kesim
20 : */
21 : #include "platform.h"
22 : #include "taler_extensions_policy.h"
23 : #include "taler_util.h"
24 : #include "taler_signatures.h"
25 : #include "taler_extensions.h"
26 : #include "stdint.h"
27 :
28 : /* head of the list of all registered extensions */
29 : static struct TALER_Extensions TE_extensions = {
30 : .next = NULL,
31 : .extension = NULL,
32 : };
33 :
34 : const struct TALER_Extensions *
35 917 : TALER_extensions_get_head ()
36 : {
37 917 : return &TE_extensions;
38 : }
39 :
40 :
41 : static enum GNUNET_GenericReturnValue
42 50 : add_extension (
43 : const struct TALER_Extension *extension)
44 : {
45 : /* Sanity checks */
46 50 : if ((NULL == extension) ||
47 50 : (NULL == extension->name) ||
48 50 : (NULL == extension->version) ||
49 50 : (NULL == extension->disable) ||
50 50 : (NULL == extension->load_config) ||
51 50 : (NULL == extension->manifest))
52 : {
53 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
54 : "invalid extension\n");
55 0 : return GNUNET_SYSERR;
56 : }
57 :
58 50 : if (NULL == TE_extensions.extension) /* first extension ?*/
59 50 : TE_extensions.extension = extension;
60 : else
61 : {
62 : struct TALER_Extensions *iter;
63 : struct TALER_Extensions *last;
64 :
65 : /* Check for collisions */
66 0 : for (iter = &TE_extensions;
67 0 : NULL != iter && NULL != iter->extension;
68 0 : iter = iter->next)
69 : {
70 0 : const struct TALER_Extension *ext = iter->extension;
71 0 : last = iter;
72 0 : if (extension->type == ext->type ||
73 0 : 0 == strcasecmp (extension->name,
74 0 : ext->name))
75 : {
76 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
77 : "extension collision for `%s'\n",
78 : extension->name);
79 0 : return GNUNET_NO;
80 : }
81 : }
82 :
83 : /* No collisions found, so add this extension to the list */
84 : {
85 0 : struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
86 0 : extn->extension = extension;
87 0 : last->next = extn;
88 : }
89 : }
90 :
91 50 : return GNUNET_OK;
92 : }
93 :
94 :
95 : const struct TALER_Extension *
96 118 : TALER_extensions_get_by_type (
97 : enum TALER_Extension_Type type)
98 : {
99 118 : for (const struct TALER_Extensions *it = &TE_extensions;
100 118 : NULL != it && NULL != it->extension;
101 0 : it = it->next)
102 : {
103 83 : if (it->extension->type == type)
104 83 : return it->extension;
105 : }
106 :
107 : /* No extension found. */
108 35 : return NULL;
109 : }
110 :
111 :
112 : bool
113 0 : TALER_extensions_is_enabled_type (
114 : enum TALER_Extension_Type type)
115 : {
116 : const struct TALER_Extension *ext =
117 0 : TALER_extensions_get_by_type (type);
118 :
119 0 : return (NULL != ext && ext->enabled);
120 : }
121 :
122 :
123 : const struct TALER_Extension *
124 0 : TALER_extensions_get_by_name (
125 : const char *name)
126 : {
127 0 : for (const struct TALER_Extensions *it = &TE_extensions;
128 0 : NULL != it;
129 0 : it = it->next)
130 : {
131 0 : if (0 == strcasecmp (name, it->extension->name))
132 0 : return it->extension;
133 : }
134 : /* No extension found, try to load it. */
135 :
136 0 : return NULL;
137 : }
138 :
139 :
140 : enum GNUNET_GenericReturnValue
141 0 : TALER_extensions_verify_manifests_signature (
142 : const json_t *manifests,
143 : struct TALER_MasterSignatureP *extensions_sig,
144 : struct TALER_MasterPublicKeyP *master_pub)
145 : {
146 : struct TALER_ExtensionManifestsHashP h_manifests;
147 :
148 0 : if (GNUNET_OK !=
149 0 : TALER_JSON_extensions_manifests_hash (manifests,
150 : &h_manifests))
151 0 : return GNUNET_SYSERR;
152 0 : if (GNUNET_OK !=
153 0 : TALER_exchange_offline_extension_manifests_hash_verify (
154 : &h_manifests,
155 : master_pub,
156 : extensions_sig))
157 0 : return GNUNET_NO;
158 0 : return GNUNET_OK;
159 : }
160 :
161 :
162 : /**
163 : * Closure used in TALER_extensions_load_taler_config during call to
164 : * GNUNET_CONFIGURATION_iterate_sections with configure_extension.
165 : */
166 : struct LoadConfClosure
167 : {
168 : const struct GNUNET_CONFIGURATION_Handle *cfg;
169 : enum GNUNET_GenericReturnValue error;
170 : };
171 :
172 :
173 : /**
174 : * Used in TALER_extensions_load_taler_config during call to
175 : * GNUNET_CONFIGURATION_iterate_sections to load the configuration
176 : * of supported extensions.
177 : *
178 : * @param cls Closure of type LoadConfClosure
179 : * @param section name of the current section
180 : */
181 : static void
182 4236 : configure_extension (
183 : void *cls,
184 : const char *section)
185 : {
186 4236 : struct LoadConfClosure *col = cls;
187 : const char *name;
188 4236 : char lib_name[1024] = {0};
189 : struct TALER_Extension *extension;
190 :
191 4236 : if (GNUNET_OK != col->error)
192 4186 : return;
193 :
194 4236 : if (0 != strncasecmp (section,
195 : TALER_EXTENSION_SECTION_PREFIX,
196 : sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1))
197 4186 : return;
198 :
199 50 : name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
200 :
201 :
202 : /* Load the extension library */
203 50 : GNUNET_snprintf (lib_name,
204 : sizeof(lib_name),
205 : "libtaler_extension_%s",
206 : name);
207 : /* Lower-case extension name, config is case-insensitive */
208 1750 : for (unsigned int i = 0; i < strlen (lib_name); i++)
209 1700 : lib_name[i] = tolower (lib_name[i]);
210 :
211 50 : extension = GNUNET_PLUGIN_load (TALER_EXCHANGE_project_data (),
212 : lib_name,
213 50 : (void *) col->cfg);
214 50 : if (NULL == extension)
215 : {
216 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
217 : "Couldn't load extension library to `%s` (section [%s]).\n",
218 : name,
219 : section);
220 0 : col->error = GNUNET_SYSERR;
221 0 : return;
222 : }
223 :
224 :
225 50 : if (GNUNET_OK != add_extension (extension))
226 : {
227 : /* FIXME[oec]: Ignoring return values here */
228 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229 : "Couldn't add extension `%s` (section [%s]).\n",
230 : name,
231 : section);
232 0 : col->error = GNUNET_SYSERR;
233 0 : GNUNET_PLUGIN_unload (
234 : lib_name,
235 0 : (void *) col->cfg);
236 0 : return;
237 : }
238 :
239 50 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
240 : "extension library '%s' loaded\n",
241 : lib_name);
242 : }
243 :
244 :
245 : static bool extensions_loaded = false;
246 :
247 : enum GNUNET_GenericReturnValue
248 87 : TALER_extensions_init (
249 : const struct GNUNET_CONFIGURATION_Handle *cfg)
250 : {
251 87 : struct LoadConfClosure col = {
252 : .cfg = cfg,
253 : .error = GNUNET_OK,
254 : };
255 :
256 87 : if (extensions_loaded)
257 0 : return GNUNET_OK;
258 :
259 87 : GNUNET_CONFIGURATION_iterate_sections (cfg,
260 : &configure_extension,
261 : &col);
262 :
263 87 : if (GNUNET_OK == col.error)
264 87 : extensions_loaded = true;
265 :
266 87 : return col.error;
267 : }
268 :
269 :
270 : enum GNUNET_GenericReturnValue
271 0 : TALER_extensions_parse_manifest (
272 : json_t *obj,
273 : int *critical,
274 : const char **version,
275 : json_t **config)
276 : {
277 : enum GNUNET_GenericReturnValue ret;
278 : struct GNUNET_JSON_Specification spec[] = {
279 0 : GNUNET_JSON_spec_boolean ("critical",
280 : critical),
281 0 : GNUNET_JSON_spec_string ("version",
282 : version),
283 0 : GNUNET_JSON_spec_json ("config",
284 : config),
285 0 : GNUNET_JSON_spec_end ()
286 : };
287 :
288 0 : *config = NULL;
289 0 : if (GNUNET_OK !=
290 0 : (ret = GNUNET_JSON_parse (obj,
291 : spec,
292 : NULL,
293 : NULL)))
294 0 : return ret;
295 0 : return GNUNET_OK;
296 : }
297 :
298 :
299 : enum GNUNET_GenericReturnValue
300 0 : TALER_extensions_load_manifests (
301 : const json_t *extensions)
302 : {
303 : const char *name;
304 : json_t *manifest;
305 :
306 0 : GNUNET_assert (NULL != extensions);
307 0 : GNUNET_assert (json_is_object (extensions));
308 :
309 0 : json_object_foreach ((json_t *) extensions, name, manifest)
310 : {
311 : int critical;
312 : const char *version;
313 : json_t *config;
314 : struct TALER_Extension *extension
315 : = (struct TALER_Extension *)
316 0 : TALER_extensions_get_by_name (name);
317 :
318 0 : if (NULL == extension)
319 : {
320 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
321 : "no such extension: %s\n",
322 : name);
323 0 : return GNUNET_SYSERR;
324 : }
325 :
326 : /* load and verify criticality, version, etc. */
327 0 : if (GNUNET_OK !=
328 0 : TALER_extensions_parse_manifest (
329 : manifest,
330 : &critical,
331 : &version,
332 : &config))
333 0 : return GNUNET_SYSERR;
334 :
335 0 : if (critical != extension->critical
336 0 : || 0 != strcmp (version,
337 : extension->version) // FIXME[oec]: libtool compare?
338 0 : || NULL == config
339 0 : || (GNUNET_OK !=
340 0 : extension->load_config (config,
341 : NULL)) )
342 0 : return GNUNET_SYSERR;
343 :
344 : /* This _should_ work now */
345 0 : if (GNUNET_OK !=
346 0 : extension->load_config (config,
347 : extension))
348 0 : return GNUNET_SYSERR;
349 :
350 0 : extension->enabled = true;
351 : }
352 :
353 : /* make sure to disable all extensions that weren't mentioned in the json */
354 0 : for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
355 0 : NULL != it;
356 0 : it = it->next)
357 : {
358 0 : if (NULL == json_object_get (extensions, it->extension->name))
359 0 : it->extension->disable ((struct TALER_Extension *) it);
360 : }
361 :
362 0 : return GNUNET_OK;
363 : }
364 :
365 :
366 : /**
367 : * Policy related
368 : */
369 : static const char *fulfillment2str[] = {
370 : [TALER_PolicyFulfillmentInitial] = "<init>",
371 : [TALER_PolicyFulfillmentReady] = "Ready",
372 : [TALER_PolicyFulfillmentSuccess] = "Success",
373 : [TALER_PolicyFulfillmentFailure] = "Failure",
374 : [TALER_PolicyFulfillmentTimeout] = "Timeout",
375 : [TALER_PolicyFulfillmentInsufficient] = "Insufficient",
376 : };
377 :
378 : const char *
379 0 : TALER_policy_fulfillment_state_str (
380 : enum TALER_PolicyFulfillmentState state)
381 : {
382 0 : GNUNET_assert (TALER_PolicyFulfillmentStateCount > state);
383 0 : return fulfillment2str[state];
384 : }
385 :
386 :
387 : enum GNUNET_GenericReturnValue
388 0 : TALER_extensions_create_policy_details (
389 : const char *currency,
390 : const json_t *policy_options,
391 : struct TALER_PolicyDetails *details,
392 : const char **error_hint)
393 : {
394 : enum GNUNET_GenericReturnValue ret;
395 : const struct TALER_Extension *extension;
396 : const json_t *jtype;
397 : const char *type;
398 :
399 0 : *error_hint = NULL;
400 :
401 0 : if ((NULL == policy_options) ||
402 0 : (! json_is_object (policy_options)))
403 : {
404 0 : *error_hint = "invalid policy object";
405 0 : return GNUNET_SYSERR;
406 : }
407 :
408 0 : jtype = json_object_get (policy_options, "type");
409 0 : if (NULL == jtype)
410 : {
411 0 : *error_hint = "no type in policy object";
412 0 : return GNUNET_SYSERR;
413 : }
414 :
415 0 : type = json_string_value (jtype);
416 0 : if (NULL == type)
417 : {
418 0 : *error_hint = "invalid type in policy object";
419 0 : return GNUNET_SYSERR;
420 : }
421 :
422 0 : extension = TALER_extensions_get_by_name (type);
423 0 : if ((NULL == extension) ||
424 0 : (NULL == extension->create_policy_details))
425 : {
426 0 : GNUNET_break (0);
427 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
428 : "Unsupported extension policy '%s' requested\n",
429 : type);
430 0 : return GNUNET_NO;
431 : }
432 :
433 : /* Set state fields in the policy details to initial values. */
434 0 : GNUNET_assert (GNUNET_OK ==
435 : TALER_amount_set_zero (currency,
436 : &details->accumulated_total));
437 0 : GNUNET_assert (GNUNET_OK ==
438 : TALER_amount_set_zero (currency,
439 : &details->policy_fee));
440 0 : details->deadline = GNUNET_TIME_UNIT_FOREVER_TS;
441 0 : details->fulfillment_state = TALER_PolicyFulfillmentInitial;
442 0 : details->no_policy_fulfillment_id = true;
443 0 : ret = extension->create_policy_details (currency,
444 : policy_options,
445 : details,
446 : error_hint);
447 0 : return ret;
448 :
449 : }
450 :
451 :
452 : /* end of extensions.c */
|