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_util.h"
23 : #include "taler_signatures.h"
24 : #include "taler_extensions.h"
25 : #include "stdint.h"
26 :
27 :
28 : /* head of the list of all registered extensions */
29 : static struct TALER_Extension *TE_extensions = NULL;
30 :
31 :
32 : const struct TALER_Extension *
33 0 : TALER_extensions_get_head ()
34 : {
35 0 : return TE_extensions;
36 : }
37 :
38 :
39 : enum GNUNET_GenericReturnValue
40 4 : TALER_extensions_add (
41 : struct TALER_Extension *extension)
42 : {
43 : /* Sanity checks */
44 4 : if ((NULL == extension) ||
45 4 : (NULL == extension->name) ||
46 4 : (NULL == extension->version) ||
47 4 : (NULL == extension->disable) ||
48 4 : (NULL == extension->test_json_config) ||
49 4 : (NULL == extension->load_json_config) ||
50 4 : (NULL == extension->config_to_json) ||
51 4 : (NULL == extension->load_taler_config))
52 : {
53 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
54 : "invalid extension\n");
55 0 : return GNUNET_SYSERR;
56 : }
57 :
58 4 : if (NULL == TE_extensions) /* first extension ?*/
59 4 : TE_extensions = (struct TALER_Extension *) extension;
60 : else
61 : {
62 : struct TALER_Extension *iter;
63 : struct TALER_Extension *last;
64 :
65 : /* Check for collisions */
66 0 : for (iter = TE_extensions; NULL != iter; iter = iter->next)
67 : {
68 0 : last = iter;
69 0 : if (extension->type == iter->type ||
70 0 : 0 == strcasecmp (extension->name,
71 0 : iter->name))
72 : {
73 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
74 : "extension collision for `%s'\n",
75 : extension->name);
76 0 : return GNUNET_NO;
77 : }
78 : }
79 :
80 : /* No collisions found, so add this extension to the list */
81 0 : last->next = extension;
82 : }
83 :
84 4 : return GNUNET_OK;
85 : }
86 :
87 :
88 : const struct TALER_Extension *
89 0 : TALER_extensions_get_by_type (
90 : enum TALER_Extension_Type type)
91 : {
92 0 : for (const struct TALER_Extension *it = TE_extensions;
93 : NULL != it;
94 0 : it = it->next)
95 : {
96 0 : if (it->type == type)
97 0 : return it;
98 : }
99 :
100 : /* No extension found. */
101 0 : return NULL;
102 : }
103 :
104 :
105 : bool
106 0 : TALER_extensions_is_enabled_type (
107 : enum TALER_Extension_Type type)
108 : {
109 : const struct TALER_Extension *ext =
110 0 : TALER_extensions_get_by_type (type);
111 :
112 0 : return (NULL != ext &&
113 0 : TALER_extensions_is_enabled (ext));
114 : }
115 :
116 :
117 : const struct TALER_Extension *
118 0 : TALER_extensions_get_by_name (
119 : const char *name)
120 : {
121 0 : for (const struct TALER_Extension *it = TE_extensions;
122 : NULL != it;
123 0 : it = it->next)
124 : {
125 0 : if (0 == strcasecmp (name, it->name))
126 0 : return it;
127 : }
128 : /* No extension found. */
129 0 : return NULL;
130 : }
131 :
132 :
133 : enum GNUNET_GenericReturnValue
134 0 : TALER_extensions_verify_json_config_signature (
135 : json_t *extensions,
136 : struct TALER_MasterSignatureP *extensions_sig,
137 : struct TALER_MasterPublicKeyP *master_pub)
138 : {
139 : struct TALER_ExtensionConfigHashP h_config;
140 :
141 0 : if (GNUNET_OK !=
142 0 : TALER_JSON_extensions_config_hash (extensions,
143 : &h_config))
144 0 : return GNUNET_SYSERR;
145 0 : if (GNUNET_OK !=
146 0 : TALER_exchange_offline_extension_config_hash_verify (
147 : &h_config,
148 : master_pub,
149 : extensions_sig))
150 0 : return GNUNET_NO;
151 0 : return GNUNET_OK;
152 : }
153 :
154 :
155 : /*
156 : * Closure used in TALER_extensions_load_taler_config during call to
157 : * GNUNET_CONFIGURATION_iterate_sections with configure_extension.
158 : */
159 : struct LoadConfClosure
160 : {
161 : const struct GNUNET_CONFIGURATION_Handle *cfg;
162 : enum GNUNET_GenericReturnValue error;
163 : };
164 :
165 :
166 : /*
167 : * Used in TALER_extensions_load_taler_config during call to
168 : * GNUNET_CONFIGURATION_iterate_sections to load the configuration
169 : * of supported extensions.
170 : *
171 : * @param cls Closure of type LoadConfClosure
172 : * @param section name of the current section
173 : */
174 : static void
175 0 : configure_extension (
176 : void *cls,
177 : const char *section)
178 : {
179 0 : struct LoadConfClosure *col = cls;
180 : const char *name;
181 : const struct TALER_Extension *extension;
182 :
183 0 : if (GNUNET_OK != col->error)
184 0 : return;
185 :
186 0 : if (0 != strncasecmp (section,
187 : TALER_EXTENSION_SECTION_PREFIX,
188 : sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1))
189 0 : return;
190 :
191 0 : name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
192 :
193 0 : if (NULL ==
194 0 : (extension = TALER_extensions_get_by_name (name)))
195 : {
196 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197 : "Unsupported extension `%s` (section [%s]).\n", name,
198 : section);
199 0 : col->error = GNUNET_SYSERR;
200 0 : return;
201 : }
202 :
203 0 : if (GNUNET_OK !=
204 0 : extension->load_taler_config (
205 : (struct TALER_Extension *) extension,
206 : col->cfg))
207 : {
208 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 : "Couldn't parse configuration for extension `%s` (section [%s]).\n",
210 : name,
211 : section);
212 0 : col->error = GNUNET_SYSERR;
213 0 : return;
214 : }
215 : }
216 :
217 :
218 : enum GNUNET_GenericReturnValue
219 0 : TALER_extensions_load_taler_config (
220 : const struct GNUNET_CONFIGURATION_Handle *cfg)
221 : {
222 0 : struct LoadConfClosure col = {
223 : .cfg = cfg,
224 : .error = GNUNET_OK,
225 : };
226 :
227 0 : GNUNET_CONFIGURATION_iterate_sections (cfg,
228 : &configure_extension,
229 : &col);
230 0 : return col.error;
231 : }
232 :
233 :
234 : enum GNUNET_GenericReturnValue
235 0 : TALER_extensions_is_json_config (
236 : json_t *obj,
237 : int *critical,
238 : const char **version,
239 : json_t **config)
240 : {
241 : enum GNUNET_GenericReturnValue ret;
242 : json_t *cfg;
243 : struct GNUNET_JSON_Specification spec[] = {
244 0 : GNUNET_JSON_spec_boolean ("critical",
245 : critical),
246 0 : GNUNET_JSON_spec_string ("version",
247 : version),
248 0 : GNUNET_JSON_spec_json ("config",
249 : &cfg),
250 0 : GNUNET_JSON_spec_end ()
251 : };
252 :
253 0 : if (GNUNET_OK !=
254 0 : (ret = GNUNET_JSON_parse (obj,
255 : spec,
256 : NULL,
257 : NULL)))
258 0 : return ret;
259 :
260 0 : *config = json_copy (cfg);
261 0 : GNUNET_JSON_parse_free (spec);
262 :
263 0 : return GNUNET_OK;
264 : }
265 :
266 :
267 : enum GNUNET_GenericReturnValue
268 0 : TALER_extensions_load_json_config (
269 : json_t *extensions)
270 : {
271 : const char*name;
272 : json_t *blob;
273 :
274 0 : GNUNET_assert (NULL != extensions);
275 0 : GNUNET_assert (json_is_object (extensions));
276 :
277 0 : json_object_foreach (extensions, name, blob)
278 : {
279 : int critical;
280 : const char *version;
281 : json_t *config;
282 : const struct TALER_Extension *extension =
283 0 : TALER_extensions_get_by_name (name);
284 :
285 0 : if (NULL == extension)
286 : {
287 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288 : "no such extension: %s\n", name);
289 0 : return GNUNET_SYSERR;
290 : }
291 :
292 : /* load and verify criticality, version, etc. */
293 0 : if (GNUNET_OK !=
294 0 : TALER_extensions_is_json_config (
295 : blob, &critical, &version, &config))
296 0 : return GNUNET_SYSERR;
297 :
298 0 : if (critical != extension->critical
299 0 : || 0 != strcmp (version, extension->version) // TODO: libtool compare?
300 0 : || NULL == config
301 0 : || GNUNET_OK != extension->test_json_config (config))
302 0 : return GNUNET_SYSERR;
303 :
304 : /* This _should_ work now */
305 0 : if (GNUNET_OK !=
306 0 : extension->load_json_config ((struct TALER_Extension *) extension,
307 : config))
308 0 : return GNUNET_SYSERR;
309 : }
310 :
311 : /* make sure to disable all extensions that weren't mentioned in the json */
312 0 : for (const struct TALER_Extension *it = TALER_extensions_get_head ();
313 : NULL != it;
314 0 : it = it->next)
315 : {
316 0 : if (NULL == json_object_get (extensions, it->name))
317 0 : it->disable ((struct TALER_Extension *) it);
318 : }
319 :
320 0 : return GNUNET_OK;
321 : }
322 :
323 :
324 : bool
325 0 : TALER_extensions_age_restriction_is_enabled ()
326 : {
327 : const struct TALER_Extension *age =
328 0 : TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
329 :
330 0 : return (NULL != age &&
331 0 : NULL != age->config_json &&
332 0 : TALER_extensions_age_restriction_is_configured ());
333 : }
334 :
335 :
336 : /* end of extensions.c */
|