LCOV - code coverage report
Current view: top level - extensions - extensions.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 41 151 27.2 %
Date: 2025-06-05 21:03:14 Functions: 5 12 41.7 %

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

Generated by: LCOV version 1.16