extensions.c (11829B)
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 "taler/platform.h" 22 #include "taler/taler_extensions_policy.h" 23 #include "taler/taler_util.h" 24 #include "taler/taler_signatures.h" 25 #include "taler/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 TALER_extensions_get_head () 36 { 37 return &TE_extensions; 38 } 39 40 41 static enum GNUNET_GenericReturnValue 42 add_extension ( 43 const struct TALER_Extension *extension) 44 { 45 /* Sanity checks */ 46 if ((NULL == extension) || 47 (NULL == extension->name) || 48 (NULL == extension->version) || 49 (NULL == extension->disable) || 50 (NULL == extension->load_config) || 51 (NULL == extension->manifest)) 52 { 53 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 54 "invalid extension\n"); 55 return GNUNET_SYSERR; 56 } 57 58 if (NULL == TE_extensions.extension) /* first extension ?*/ 59 TE_extensions.extension = extension; 60 else 61 { 62 struct TALER_Extensions *iter; 63 struct TALER_Extensions *last; 64 65 /* Check for collisions */ 66 for (iter = &TE_extensions; 67 NULL != iter && NULL != iter->extension; 68 iter = iter->next) 69 { 70 const struct TALER_Extension *ext = iter->extension; 71 last = iter; 72 if (extension->type == ext->type || 73 0 == strcasecmp (extension->name, 74 ext->name)) 75 { 76 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 77 "extension collision for `%s'\n", 78 extension->name); 79 return GNUNET_NO; 80 } 81 } 82 83 /* No collisions found, so add this extension to the list */ 84 { 85 struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions); 86 extn->extension = extension; 87 last->next = extn; 88 } 89 } 90 91 return GNUNET_OK; 92 } 93 94 95 const struct TALER_Extension * 96 TALER_extensions_get_by_type ( 97 enum TALER_Extension_Type type) 98 { 99 for (const struct TALER_Extensions *it = &TE_extensions; 100 NULL != it && NULL != it->extension; 101 it = it->next) 102 { 103 if (it->extension->type == type) 104 return it->extension; 105 } 106 107 /* No extension found. */ 108 return NULL; 109 } 110 111 112 bool 113 TALER_extensions_is_enabled_type ( 114 enum TALER_Extension_Type type) 115 { 116 const struct TALER_Extension *ext = 117 TALER_extensions_get_by_type (type); 118 119 return (NULL != ext && ext->enabled); 120 } 121 122 123 const struct TALER_Extension * 124 TALER_extensions_get_by_name ( 125 const char *name) 126 { 127 for (const struct TALER_Extensions *it = &TE_extensions; 128 NULL != it; 129 it = it->next) 130 { 131 if (0 == strcasecmp (name, it->extension->name)) 132 return it->extension; 133 } 134 /* No extension found, try to load it. */ 135 136 return NULL; 137 } 138 139 140 enum GNUNET_GenericReturnValue 141 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 if (GNUNET_OK != 149 TALER_JSON_extensions_manifests_hash (manifests, 150 &h_manifests)) 151 return GNUNET_SYSERR; 152 if (GNUNET_OK != 153 TALER_exchange_offline_extension_manifests_hash_verify ( 154 &h_manifests, 155 master_pub, 156 extensions_sig)) 157 return GNUNET_NO; 158 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 configure_extension ( 183 void *cls, 184 const char *section) 185 { 186 struct LoadConfClosure *col = cls; 187 const char *name; 188 char lib_name[1024] = {0}; 189 struct TALER_Extension *extension; 190 191 if (GNUNET_OK != col->error) 192 return; 193 194 if (0 != strncasecmp (section, 195 TALER_EXTENSION_SECTION_PREFIX, 196 sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1)) 197 return; 198 199 name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1; 200 201 202 /* Load the extension library */ 203 GNUNET_snprintf (lib_name, 204 sizeof(lib_name), 205 "libtaler_extension_%s", 206 name); 207 /* Lower-case extension name, config is case-insensitive */ 208 for (unsigned int i = 0; i < strlen (lib_name); i++) 209 lib_name[i] = tolower (lib_name[i]); 210 211 extension = GNUNET_PLUGIN_load (TALER_EXCHANGE_project_data (), 212 lib_name, 213 (void *) col->cfg); 214 if (NULL == extension) 215 { 216 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 217 "Couldn't load extension library to `%s` (section [%s]).\n", 218 name, 219 section); 220 col->error = GNUNET_SYSERR; 221 return; 222 } 223 224 225 if (GNUNET_OK != add_extension (extension)) 226 { 227 /* FIXME[oec]: Ignoring return values here */ 228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 229 "Couldn't add extension `%s` (section [%s]).\n", 230 name, 231 section); 232 col->error = GNUNET_SYSERR; 233 GNUNET_PLUGIN_unload ( 234 lib_name, 235 (void *) col->cfg); 236 return; 237 } 238 239 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 TALER_extensions_init ( 249 const struct GNUNET_CONFIGURATION_Handle *cfg) 250 { 251 struct LoadConfClosure col = { 252 .cfg = cfg, 253 .error = GNUNET_OK, 254 }; 255 256 if (extensions_loaded) 257 return GNUNET_OK; 258 259 GNUNET_CONFIGURATION_iterate_sections (cfg, 260 &configure_extension, 261 &col); 262 263 if (GNUNET_OK == col.error) 264 extensions_loaded = true; 265 266 return col.error; 267 } 268 269 270 enum GNUNET_GenericReturnValue 271 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 GNUNET_JSON_spec_boolean ("critical", 280 critical), 281 GNUNET_JSON_spec_string ("version", 282 version), 283 GNUNET_JSON_spec_json ("config", 284 config), 285 GNUNET_JSON_spec_end () 286 }; 287 288 *config = NULL; 289 if (GNUNET_OK != 290 (ret = GNUNET_JSON_parse (obj, 291 spec, 292 NULL, 293 NULL))) 294 return ret; 295 return GNUNET_OK; 296 } 297 298 299 enum GNUNET_GenericReturnValue 300 TALER_extensions_load_manifests ( 301 const json_t *extensions) 302 { 303 const char *name; 304 json_t *manifest; 305 306 GNUNET_assert (NULL != extensions); 307 GNUNET_assert (json_is_object (extensions)); 308 309 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 TALER_extensions_get_by_name (name); 317 318 if (NULL == extension) 319 { 320 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 321 "no such extension: %s\n", 322 name); 323 return GNUNET_SYSERR; 324 } 325 326 /* load and verify criticality, version, etc. */ 327 if (GNUNET_OK != 328 TALER_extensions_parse_manifest ( 329 manifest, 330 &critical, 331 &version, 332 &config)) 333 return GNUNET_SYSERR; 334 335 if (critical != extension->critical 336 || 0 != strcmp (version, 337 extension->version) // FIXME[oec]: libtool compare? 338 || NULL == config 339 || (GNUNET_OK != 340 extension->load_config (config, 341 NULL)) ) 342 return GNUNET_SYSERR; 343 344 /* This _should_ work now */ 345 if (GNUNET_OK != 346 extension->load_config (config, 347 extension)) 348 return GNUNET_SYSERR; 349 350 extension->enabled = true; 351 } 352 353 /* make sure to disable all extensions that weren't mentioned in the json */ 354 for (const struct TALER_Extensions *it = TALER_extensions_get_head (); 355 NULL != it; 356 it = it->next) 357 { 358 if (NULL == json_object_get (extensions, it->extension->name)) 359 it->extension->disable ((struct TALER_Extension *) it); 360 } 361 362 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 TALER_policy_fulfillment_state_str ( 380 enum TALER_PolicyFulfillmentState state) 381 { 382 GNUNET_assert (TALER_PolicyFulfillmentStateCount > state); 383 return fulfillment2str[state]; 384 } 385 386 387 enum GNUNET_GenericReturnValue 388 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 *error_hint = NULL; 400 401 if ((NULL == policy_options) || 402 (! json_is_object (policy_options))) 403 { 404 *error_hint = "invalid policy object"; 405 return GNUNET_SYSERR; 406 } 407 408 jtype = json_object_get (policy_options, "type"); 409 if (NULL == jtype) 410 { 411 *error_hint = "no type in policy object"; 412 return GNUNET_SYSERR; 413 } 414 415 type = json_string_value (jtype); 416 if (NULL == type) 417 { 418 *error_hint = "invalid type in policy object"; 419 return GNUNET_SYSERR; 420 } 421 422 extension = TALER_extensions_get_by_name (type); 423 if ((NULL == extension) || 424 (NULL == extension->create_policy_details)) 425 { 426 GNUNET_break (0); 427 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 428 "Unsupported extension policy '%s' requested\n", 429 type); 430 return GNUNET_NO; 431 } 432 433 /* Set state fields in the policy details to initial values. */ 434 GNUNET_assert (GNUNET_OK == 435 TALER_amount_set_zero (currency, 436 &details->accumulated_total)); 437 GNUNET_assert (GNUNET_OK == 438 TALER_amount_set_zero (currency, 439 &details->policy_fee)); 440 details->deadline = GNUNET_TIME_UNIT_FOREVER_TS; 441 details->fulfillment_state = TALER_PolicyFulfillmentInitial; 442 details->no_policy_fulfillment_id = true; 443 ret = extension->create_policy_details (currency, 444 policy_options, 445 details, 446 error_hint); 447 return ret; 448 449 } 450 451 452 /* end of extensions.c */