exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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 */