exchange

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

plugin_kyclogic_oauth2.c (60335B)


      1 /*
      2   This file is part of GNU Taler
      3   Copyright (C) 2022-2024 Taler Systems SA
      4 
      5   Taler is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Taler; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file plugin_kyclogic_oauth2.c
     18  * @brief oauth2.0 based authentication flow logic
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/taler_kyclogic_plugin.h"
     22 #include "taler/taler_mhd_lib.h"
     23 #include "taler/taler_templating_lib.h"
     24 #include "taler/taler_curl_lib.h"
     25 #include "taler/taler_json_lib.h"
     26 #include <regex.h>
     27 #include "taler/taler_util.h"
     28 
     29 /**
     30  * Set to 1 to get extra-verbose, possibly privacy-sensitive
     31  * data in the logs.
     32  */
     33 #define DEBUG 0
     34 
     35 /**
     36  * Saves the state of a plugin.
     37  */
     38 struct PluginState
     39 {
     40 
     41   /**
     42    * Our global configuration.
     43    */
     44   const struct GNUNET_CONFIGURATION_Handle *cfg;
     45 
     46   /**
     47    * Our base URL.
     48    */
     49   char *exchange_base_url;
     50 
     51   /**
     52    * Context for CURL operations (useful to the event loop)
     53    */
     54   struct GNUNET_CURL_Context *curl_ctx;
     55 
     56   /**
     57    * Context for integrating @e curl_ctx with the
     58    * GNUnet event loop.
     59    */
     60   struct GNUNET_CURL_RescheduleContext *curl_rc;
     61 
     62 };
     63 
     64 
     65 /**
     66  * Keeps the plugin-specific state for
     67  * a given configuration section.
     68  */
     69 struct TALER_KYCLOGIC_ProviderDetails
     70 {
     71 
     72   /**
     73    * Overall plugin state.
     74    */
     75   struct PluginState *ps;
     76 
     77   /**
     78    * Configuration section that configured us.
     79    */
     80   char *section;
     81 
     82   /**
     83    * URL of the Challenger ``/setup`` endpoint for
     84    * approving address validations. NULL if not used.
     85    */
     86   char *setup_url;
     87 
     88   /**
     89    * URL of the OAuth2.0 endpoint for KYC checks.
     90    */
     91   char *authorize_url;
     92 
     93   /**
     94    * URL of the OAuth2.0 endpoint for KYC checks.
     95    * (token/auth)
     96    */
     97   char *token_url;
     98 
     99   /**
    100    * URL of the user info access endpoint.
    101    */
    102   char *info_url;
    103 
    104   /**
    105    * Our client ID for OAuth2.0.
    106    */
    107   char *client_id;
    108 
    109   /**
    110    * Our client secret for OAuth2.0.
    111    */
    112   char *client_secret;
    113 
    114   /**
    115    * OAuth2 scope, NULL if not used
    116    */
    117   char *scope;
    118 
    119   /**
    120    * Where to redirect clients after the
    121    * Web-based KYC process is done?
    122    */
    123   char *post_kyc_redirect_url;
    124 
    125   /**
    126    * Name of the program we use to convert outputs
    127    * from OAuth2 outputs into our JSON inputs.
    128    */
    129   char *conversion_binary;
    130 
    131   /**
    132    * Validity time for a successful KYC process.
    133    */
    134   struct GNUNET_TIME_Relative validity;
    135 
    136   /**
    137    * Set to true if we are operating in DEBUG
    138    * mode and may return private details in HTML
    139    * responses to make diagnostics easier.
    140    */
    141   bool debug_mode;
    142 };
    143 
    144 
    145 /**
    146  * Handle for an initiation operation.
    147  */
    148 struct TALER_KYCLOGIC_InitiateHandle
    149 {
    150 
    151   /**
    152    * Hash of the payto:// URI we are initiating
    153    * the KYC for.
    154    */
    155   struct TALER_NormalizedPaytoHashP h_payto;
    156 
    157   /**
    158    * UUID being checked.
    159    */
    160   uint64_t legitimization_uuid;
    161 
    162   /**
    163    * Our configuration details.
    164    */
    165   const struct TALER_KYCLOGIC_ProviderDetails *pd;
    166 
    167   /**
    168    * The task for asynchronous response generation.
    169    */
    170   struct GNUNET_SCHEDULER_Task *task;
    171 
    172   /**
    173    * Handle for the OAuth 2.0 setup request.
    174    */
    175   struct GNUNET_CURL_Job *job;
    176 
    177   /**
    178    * Continuation to call.
    179    */
    180   TALER_KYCLOGIC_InitiateCallback cb;
    181 
    182   /**
    183    * Closure for @a cb.
    184    */
    185   void *cb_cls;
    186 
    187   /**
    188    * Initial address to pass to the KYC provider on ``/setup``.
    189    */
    190   json_t *initial_address;
    191 
    192   /**
    193    * Expiration reported by a dynamic /setup endpoint.
    194    */
    195   struct GNUNET_TIME_Timestamp process_expiration;
    196 
    197   /**
    198    * Context for #TEH_curl_easy_post(). Keeps the data that must
    199    * persist for Curl to make the upload.
    200    */
    201   struct TALER_CURL_PostContext ctx;
    202 
    203 };
    204 
    205 
    206 /**
    207  * Handle for an KYC proof operation.
    208  */
    209 struct TALER_KYCLOGIC_ProofHandle
    210 {
    211 
    212   /**
    213    * Our configuration details.
    214    */
    215   const struct TALER_KYCLOGIC_ProviderDetails *pd;
    216 
    217   /**
    218    * HTTP connection we are processing.
    219    */
    220   struct MHD_Connection *connection;
    221 
    222   /**
    223    * Handle to an external process that converts the
    224    * Persona response to our internal format.
    225    */
    226   struct TALER_JSON_ExternalConversion *ec;
    227 
    228   /**
    229    * Hash of the payto URI that this is about.
    230    */
    231   struct TALER_NormalizedPaytoHashP h_payto;
    232 
    233   /**
    234    * Continuation to call.
    235    */
    236   TALER_KYCLOGIC_ProofCallback cb;
    237 
    238   /**
    239    * Closure for @e cb.
    240    */
    241   void *cb_cls;
    242 
    243   /**
    244    * Curl request we are running to the OAuth 2.0 service.
    245    */
    246   CURL *eh;
    247 
    248   /**
    249    * Body for the @e eh POST request.
    250    */
    251   char *post_body;
    252 
    253   /**
    254    * KYC attributes returned about the user by the OAuth 2.0 server.
    255    */
    256   json_t *attributes;
    257 
    258   /**
    259    * Response to return.
    260    */
    261   struct MHD_Response *response;
    262 
    263   /**
    264    * The task for asynchronous response generation.
    265    */
    266   struct GNUNET_SCHEDULER_Task *task;
    267 
    268   /**
    269    * Handle for the OAuth 2.0 CURL request.
    270    */
    271   struct GNUNET_CURL_Job *job;
    272 
    273   /**
    274    * User ID to return, the 'id' from OAuth.
    275    */
    276   char *provider_user_id;
    277 
    278   /**
    279    * Legitimization ID to return, the 64-bit row ID
    280    * as a string.
    281    */
    282   char provider_legitimization_id[32];
    283 
    284   /**
    285    * KYC status to return.
    286    */
    287   enum TALER_KYCLOGIC_KycStatus status;
    288 
    289   /**
    290    * HTTP status to return.
    291    */
    292   unsigned int http_status;
    293 
    294 
    295 };
    296 
    297 
    298 /**
    299  * Handle for an KYC Web hook operation.
    300  */
    301 struct TALER_KYCLOGIC_WebhookHandle
    302 {
    303 
    304   /**
    305    * Continuation to call when done.
    306    */
    307   TALER_KYCLOGIC_WebhookCallback cb;
    308 
    309   /**
    310    * Closure for @a cb.
    311    */
    312   void *cb_cls;
    313 
    314   /**
    315    * Task for asynchronous execution.
    316    */
    317   struct GNUNET_SCHEDULER_Task *task;
    318 
    319   /**
    320    * Overall plugin state.
    321    */
    322   struct PluginState *ps;
    323 };
    324 
    325 
    326 /**
    327  * Release configuration resources previously loaded
    328  *
    329  * @param[in] pd configuration to release
    330  */
    331 static void
    332 oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
    333 {
    334   GNUNET_free (pd->section);
    335   GNUNET_free (pd->token_url);
    336   GNUNET_free (pd->setup_url);
    337   GNUNET_free (pd->authorize_url);
    338   GNUNET_free (pd->info_url);
    339   GNUNET_free (pd->client_id);
    340   GNUNET_free (pd->client_secret);
    341   GNUNET_free (pd->scope);
    342   GNUNET_free (pd->post_kyc_redirect_url);
    343   GNUNET_free (pd->conversion_binary);
    344   GNUNET_free (pd);
    345 }
    346 
    347 
    348 /**
    349  * Load the configuration of the KYC provider.
    350  *
    351  * @param cls closure
    352  * @param provider_section_name configuration section to parse
    353  * @return NULL if configuration is invalid
    354  */
    355 static struct TALER_KYCLOGIC_ProviderDetails *
    356 oauth2_load_configuration (void *cls,
    357                            const char *provider_section_name)
    358 {
    359   struct PluginState *ps = cls;
    360   struct TALER_KYCLOGIC_ProviderDetails *pd;
    361   char *s;
    362 
    363   pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
    364   pd->ps = ps;
    365   pd->section = GNUNET_strdup (provider_section_name);
    366   if (GNUNET_OK !=
    367       GNUNET_CONFIGURATION_get_value_time (ps->cfg,
    368                                            provider_section_name,
    369                                            "KYC_OAUTH2_VALIDITY",
    370                                            &pd->validity))
    371   {
    372     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    373                                provider_section_name,
    374                                "KYC_OAUTH2_VALIDITY");
    375     oauth2_unload_configuration (pd);
    376     return NULL;
    377   }
    378 
    379   if (GNUNET_OK !=
    380       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    381                                              provider_section_name,
    382                                              "KYC_OAUTH2_CLIENT_ID",
    383                                              &s))
    384   {
    385     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    386                                provider_section_name,
    387                                "KYC_OAUTH2_CLIENT_ID");
    388     oauth2_unload_configuration (pd);
    389     return NULL;
    390   }
    391   pd->client_id = s;
    392 
    393   if (GNUNET_OK ==
    394       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    395                                              provider_section_name,
    396                                              "KYC_OAUTH2_SCOPE",
    397                                              &s))
    398   {
    399     pd->scope = s;
    400   }
    401 
    402   if (GNUNET_OK !=
    403       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    404                                              provider_section_name,
    405                                              "KYC_OAUTH2_TOKEN_URL",
    406                                              &s))
    407   {
    408     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    409                                provider_section_name,
    410                                "KYC_OAUTH2_TOKEN_URL");
    411     oauth2_unload_configuration (pd);
    412     return NULL;
    413   }
    414   if ( (! TALER_url_valid_charset (s)) ||
    415        ( (0 != strncasecmp (s,
    416                             "http://",
    417                             strlen ("http://"))) &&
    418          (0 != strncasecmp (s,
    419                             "https://",
    420                             strlen ("https://"))) ) )
    421   {
    422     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    423                                provider_section_name,
    424                                "KYC_OAUTH2_TOKEN_URL",
    425                                "not a valid URL");
    426     GNUNET_free (s);
    427     oauth2_unload_configuration (pd);
    428     return NULL;
    429   }
    430   pd->token_url = s;
    431 
    432   if (GNUNET_OK !=
    433       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    434                                              provider_section_name,
    435                                              "KYC_OAUTH2_AUTHORIZE_URL",
    436                                              &s))
    437   {
    438     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    439                                provider_section_name,
    440                                "KYC_OAUTH2_AUTHORIZE_URL");
    441     oauth2_unload_configuration (pd);
    442     return NULL;
    443   }
    444   if ( (! TALER_url_valid_charset (s)) ||
    445        ( (0 != strncasecmp (s,
    446                             "http://",
    447                             strlen ("http://"))) &&
    448          (0 != strncasecmp (s,
    449                             "https://",
    450                             strlen ("https://"))) ) )
    451   {
    452     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    453                                provider_section_name,
    454                                "KYC_OAUTH2_AUTHORIZE_URL",
    455                                "not a valid URL");
    456     oauth2_unload_configuration (pd);
    457     GNUNET_free (s);
    458     return NULL;
    459   }
    460   if (NULL != strchr (s, '#'))
    461   {
    462     const char *extra = strchr (s, '#');
    463     const char *slash = strrchr (s, '/');
    464 
    465     if ( (0 != strcasecmp (extra,
    466                            "#setup")) ||
    467          (NULL == slash) )
    468     {
    469       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    470                                  provider_section_name,
    471                                  "KYC_OAUTH2_AUTHORIZE_URL",
    472                                  "not a valid authorze URL (bad fragment)");
    473       oauth2_unload_configuration (pd);
    474       GNUNET_free (s);
    475       return NULL;
    476     }
    477     pd->authorize_url = GNUNET_strndup (s,
    478                                         extra - s);
    479     GNUNET_asprintf (&pd->setup_url,
    480                      "%.*s/setup/%s",
    481                      (int) (slash - s),
    482                      s,
    483                      pd->client_id);
    484     GNUNET_free (s);
    485   }
    486   else
    487   {
    488     pd->authorize_url = s;
    489   }
    490 
    491   if (GNUNET_OK !=
    492       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    493                                              provider_section_name,
    494                                              "KYC_OAUTH2_INFO_URL",
    495                                              &s))
    496   {
    497     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    498                                provider_section_name,
    499                                "KYC_OAUTH2_INFO_URL");
    500     oauth2_unload_configuration (pd);
    501     return NULL;
    502   }
    503   if ( (! TALER_url_valid_charset (s)) ||
    504        ( (0 != strncasecmp (s,
    505                             "http://",
    506                             strlen ("http://"))) &&
    507          (0 != strncasecmp (s,
    508                             "https://",
    509                             strlen ("https://"))) ) )
    510   {
    511     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    512                                provider_section_name,
    513                                "KYC_INFO_URL",
    514                                "not a valid URL");
    515     GNUNET_free (s);
    516     oauth2_unload_configuration (pd);
    517     return NULL;
    518   }
    519   pd->info_url = s;
    520 
    521   if (GNUNET_OK !=
    522       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    523                                              provider_section_name,
    524                                              "KYC_OAUTH2_CLIENT_SECRET",
    525                                              &s))
    526   {
    527     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    528                                provider_section_name,
    529                                "KYC_OAUTH2_CLIENT_SECRET");
    530     oauth2_unload_configuration (pd);
    531     return NULL;
    532   }
    533   pd->client_secret = s;
    534 
    535   if (GNUNET_OK !=
    536       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    537                                              provider_section_name,
    538                                              "KYC_OAUTH2_POST_URL",
    539                                              &s))
    540   {
    541     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    542                                provider_section_name,
    543                                "KYC_OAUTH2_POST_URL");
    544     oauth2_unload_configuration (pd);
    545     return NULL;
    546   }
    547   pd->post_kyc_redirect_url = s;
    548 
    549   if (GNUNET_OK !=
    550       GNUNET_CONFIGURATION_get_value_string (ps->cfg,
    551                                              provider_section_name,
    552                                              "KYC_OAUTH2_CONVERTER_HELPER",
    553                                              &pd->conversion_binary))
    554   {
    555     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    556                                provider_section_name,
    557                                "KYC_OAUTH2_CONVERTER_HELPER");
    558     oauth2_unload_configuration (pd);
    559     return NULL;
    560   }
    561   if (GNUNET_OK ==
    562       GNUNET_CONFIGURATION_get_value_yesno (ps->cfg,
    563                                             provider_section_name,
    564                                             "KYC_OAUTH2_DEBUG_MODE"))
    565     pd->debug_mode = true;
    566 
    567   return pd;
    568 }
    569 
    570 
    571 /**
    572  * Cancel KYC check initiation.
    573  *
    574  * @param[in] ih handle of operation to cancel
    575  */
    576 static void
    577 oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
    578 {
    579   if (NULL != ih->task)
    580   {
    581     GNUNET_SCHEDULER_cancel (ih->task);
    582     ih->task = NULL;
    583   }
    584   if (NULL != ih->job)
    585   {
    586     GNUNET_CURL_job_cancel (ih->job);
    587     ih->job = NULL;
    588   }
    589   TALER_curl_easy_post_finished (&ih->ctx);
    590   json_decref (ih->initial_address);
    591   GNUNET_free (ih);
    592 }
    593 
    594 
    595 /**
    596  * Logic to asynchronously return the response for
    597  * how to begin the OAuth2.0 checking process to
    598  * the client.
    599  *
    600  * @param ih process to redirect for
    601  * @param authorize_url authorization URL to use
    602  */
    603 static void
    604 initiate_with_url (struct TALER_KYCLOGIC_InitiateHandle *ih,
    605                    const char *authorize_url)
    606 {
    607 
    608   const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
    609   struct PluginState *ps = pd->ps;
    610   char *hps;
    611   char *url;
    612   char legi_s[42];
    613 
    614   GNUNET_snprintf (legi_s,
    615                    sizeof (legi_s),
    616                    "%llu",
    617                    (unsigned long long) ih->legitimization_uuid);
    618   hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
    619                                              sizeof (ih->h_payto));
    620   {
    621     char *redirect_uri_encoded;
    622     char *client_id_encoded;
    623     char *scope_encoded;
    624 
    625     {
    626       char *redirect_uri;
    627 
    628       GNUNET_asprintf (&redirect_uri,
    629                        "%skyc-proof/%s",
    630                        ps->exchange_base_url,
    631                        &pd->section[strlen ("kyc-provider-")]);
    632       redirect_uri_encoded = TALER_urlencode (redirect_uri);
    633       GNUNET_free (redirect_uri);
    634     }
    635     client_id_encoded = TALER_urlencode (pd->client_id);
    636     scope_encoded = TALER_urlencode (NULL != pd->scope
    637                                      ? pd->scope
    638                                      : "");
    639     GNUNET_asprintf (&url,
    640                      "%s?response_type=code&client_id=%s&redirect_uri=%s&state=%s&scope=%s",
    641                      authorize_url,
    642                      client_id_encoded,
    643                      redirect_uri_encoded,
    644                      hps,
    645                      scope_encoded);
    646     GNUNET_free (scope_encoded);
    647     GNUNET_free (client_id_encoded);
    648     GNUNET_free (redirect_uri_encoded);
    649   }
    650   ih->cb (ih->cb_cls,
    651           TALER_EC_NONE,
    652           url,
    653           NULL /* unknown user_id here */,
    654           legi_s,
    655           NULL /* no error */);
    656   GNUNET_free (url);
    657   GNUNET_free (hps);
    658   oauth2_initiate_cancel (ih);
    659 }
    660 
    661 
    662 /**
    663  * After we are done with the CURL interaction we
    664  * need to update our database state with the information
    665  * retrieved.
    666  *
    667  * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
    668  * @param response_code HTTP response code from server, 0 on hard error
    669  * @param response in JSON, NULL if response was not in JSON format
    670  */
    671 static void
    672 handle_curl_setup_finished (void *cls,
    673                             long response_code,
    674                             const void *response)
    675 {
    676   struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
    677   const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
    678   const json_t *j = response;
    679 
    680   ih->job = NULL;
    681   switch (response_code)
    682   {
    683   case 0:
    684     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    685                 "/setup URL failed to return HTTP response\n");
    686     ih->cb (ih->cb_cls,
    687             TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
    688             NULL,
    689             NULL,
    690             NULL,
    691             "/setup request to OAuth 2.0 backend returned no response");
    692     oauth2_initiate_cancel (ih);
    693     return;
    694   case MHD_HTTP_OK:
    695     {
    696       const char *nonce;
    697       bool no_expiration;
    698       struct GNUNET_JSON_Specification spec[] = {
    699         GNUNET_JSON_spec_string ("nonce",
    700                                  &nonce),
    701         GNUNET_JSON_spec_mark_optional (
    702           GNUNET_JSON_spec_timestamp ("expires",
    703                                       &ih->process_expiration),
    704           &no_expiration),
    705         GNUNET_JSON_spec_end ()
    706       };
    707       enum GNUNET_GenericReturnValue res;
    708       const char *emsg;
    709       unsigned int line;
    710       char *url;
    711 
    712       res = GNUNET_JSON_parse (j,
    713                                spec,
    714                                &emsg,
    715                                &line);
    716       if (GNUNET_OK != res)
    717       {
    718         GNUNET_break_op (0);
    719         json_dumpf (j,
    720                     stderr,
    721                     JSON_INDENT (2));
    722         ih->cb (ih->cb_cls,
    723                 TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
    724                 NULL,
    725                 NULL,
    726                 NULL,
    727                 "Unexpected response from KYC gateway: setup must return a nonce");
    728         oauth2_initiate_cancel (ih);
    729         return;
    730       }
    731       if ( (! no_expiration) &&
    732            GNUNET_TIME_timestamp_cmp (
    733              ih->process_expiration,
    734              <=,
    735              GNUNET_TIME_timestamp_get ()) )
    736       {
    737         GNUNET_break_op (0);
    738         ih->cb (ih->cb_cls,
    739                 TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
    740                 NULL,
    741                 NULL,
    742                 NULL,
    743                 "KYC gateway returned an expired setup process");
    744         GNUNET_JSON_parse_free (spec);
    745         oauth2_initiate_cancel (ih);
    746         return;
    747       }
    748       {
    749         char *nonce_encoded;
    750 
    751         nonce_encoded = TALER_urlencode (nonce);
    752         GNUNET_asprintf (&url,
    753                          "%s/%s",
    754                          pd->authorize_url,
    755                          nonce_encoded);
    756         GNUNET_free (nonce_encoded);
    757       }
    758       initiate_with_url (ih,
    759                          url);
    760       GNUNET_free (url);
    761       return;
    762     }
    763     break;
    764   default:
    765     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    766                 "/setup URL returned HTTP status %u\n",
    767                 (unsigned int) response_code);
    768     ih->cb (ih->cb_cls,
    769             TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
    770             NULL,
    771             NULL,
    772             NULL,
    773             "/setup request to OAuth 2.0 backend returned unexpected HTTP status code");
    774     oauth2_initiate_cancel (ih);
    775     return;
    776   }
    777 }
    778 
    779 
    780 static struct GNUNET_TIME_Timestamp
    781 oauth2_initiate_get_expiration (
    782   const struct TALER_KYCLOGIC_InitiateHandle *ih)
    783 {
    784   return ih->process_expiration;
    785 }
    786 
    787 
    788 /**
    789  * Logic to asynchronously return the response for how to begin the OAuth2.0
    790  * checking process to the client.  May first request a dynamic URL via
    791  * ``/setup`` if configured to use a client-authenticated setup process.
    792  *
    793  * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
    794  */
    795 static void
    796 initiate_task (void *cls)
    797 {
    798   struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
    799   const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
    800   struct PluginState *ps = pd->ps;
    801   CURL *eh;
    802 
    803   ih->task = NULL;
    804   if (NULL == pd->setup_url)
    805   {
    806     initiate_with_url (ih,
    807                        pd->authorize_url);
    808     return;
    809   }
    810   eh = curl_easy_init ();
    811   if (NULL == eh)
    812   {
    813     GNUNET_break (0);
    814     ih->cb (ih->cb_cls,
    815             TALER_EC_GENERIC_ALLOCATION_FAILURE,
    816             NULL,
    817             NULL,
    818             NULL,
    819             "curl_easy_init() failed");
    820     oauth2_initiate_cancel (ih);
    821     return;
    822   }
    823   GNUNET_assert (CURLE_OK ==
    824                  curl_easy_setopt (eh,
    825                                    CURLOPT_URL,
    826                                    pd->setup_url));
    827 #if DEBUG
    828   GNUNET_assert (CURLE_OK ==
    829                  curl_easy_setopt (eh,
    830                                    CURLOPT_VERBOSE,
    831                                    1));
    832 #endif
    833   if (NULL == ih->initial_address)
    834   {
    835     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    836                 "Staring OAuth 2.0 without initial address\n");
    837     GNUNET_assert (CURLE_OK ==
    838                    curl_easy_setopt (eh,
    839                                      CURLOPT_POST,
    840                                      1));
    841     GNUNET_assert (CURLE_OK ==
    842                    curl_easy_setopt (eh,
    843                                      CURLOPT_POSTFIELDS,
    844                                      ""));
    845     GNUNET_assert (CURLE_OK ==
    846                    curl_easy_setopt (eh,
    847                                      CURLOPT_POSTFIELDSIZE,
    848                                      (long) 0));
    849   }
    850   else
    851   {
    852     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    853                 "Staring OAuth 2.0 with initial address\n");
    854 #if DEBUG
    855     json_dumpf (ih->initial_address,
    856                 stderr,
    857                 JSON_INDENT (2));
    858     fprintf (stderr,
    859              "\n");
    860 #endif
    861     if (GNUNET_OK !=
    862         TALER_curl_easy_post (&ih->ctx,
    863                               eh,
    864                               ih->initial_address))
    865     {
    866       curl_easy_cleanup (eh);
    867       ih->cb (ih->cb_cls,
    868               TALER_EC_GENERIC_ALLOCATION_FAILURE,
    869               NULL,
    870               NULL,
    871               NULL,
    872               "TALER_curl_easy_post() failed");
    873       oauth2_initiate_cancel (ih);
    874       return;
    875     }
    876   }
    877   GNUNET_assert (CURLE_OK ==
    878                  curl_easy_setopt (eh,
    879                                    CURLOPT_FOLLOWLOCATION,
    880                                    1L));
    881   GNUNET_assert (CURLE_OK ==
    882                  curl_easy_setopt (eh,
    883                                    CURLOPT_MAXREDIRS,
    884                                    5L));
    885   ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
    886                                   eh,
    887                                   ih->ctx.headers,
    888                                   &handle_curl_setup_finished,
    889                                   ih);
    890   {
    891     char *hdr;
    892     struct curl_slist *slist;
    893 
    894     GNUNET_asprintf (&hdr,
    895                      "%s: Bearer %s",
    896                      MHD_HTTP_HEADER_AUTHORIZATION,
    897                      pd->client_secret);
    898     slist = curl_slist_append (NULL,
    899                                hdr);
    900     GNUNET_CURL_extend_headers (ih->job,
    901                                 slist);
    902     curl_slist_free_all (slist);
    903     GNUNET_free (hdr);
    904   }
    905 }
    906 
    907 
    908 /**
    909  * Initiate KYC check.
    910  *
    911  * @param cls the @e cls of this struct with the plugin-specific state
    912  * @param pd provider configuration details
    913  * @param account_id which account to trigger process for
    914  * @param legitimization_uuid unique ID for the legitimization process
    915  * @param context additional contextual information for the legi process
    916  * @param cb function to call with the result
    917  * @param cb_cls closure for @a cb
    918  * @return handle to cancel operation early
    919  */
    920 static struct TALER_KYCLOGIC_InitiateHandle *
    921 oauth2_initiate (void *cls,
    922                  const struct TALER_KYCLOGIC_ProviderDetails *pd,
    923                  const struct TALER_NormalizedPaytoHashP *account_id,
    924                  uint64_t legitimization_uuid,
    925                  const json_t *context,
    926                  TALER_KYCLOGIC_InitiateCallback cb,
    927                  void *cb_cls)
    928 {
    929   struct TALER_KYCLOGIC_InitiateHandle *ih;
    930 
    931   (void) cls;
    932   ih = GNUNET_new (struct TALER_KYCLOGIC_InitiateHandle);
    933   ih->legitimization_uuid = legitimization_uuid;
    934   ih->cb = cb;
    935   ih->cb_cls = cb_cls;
    936   ih->h_payto = *account_id;
    937   ih->pd = pd;
    938   ih->task = GNUNET_SCHEDULER_add_now (&initiate_task,
    939                                        ih);
    940   if (NULL != context)
    941   {
    942     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    943                 "Initiating OAuth2 validation with context\n");
    944 #if DEBUG
    945     json_dumpf (context,
    946                 stderr,
    947                 JSON_INDENT (2));
    948     fprintf (stderr,
    949              "\n");
    950 #endif
    951     ih->initial_address = json_incref (json_object_get (context,
    952                                                         "initial_address"));
    953   }
    954   return ih;
    955 }
    956 
    957 
    958 /**
    959  * Cancel KYC proof.
    960  *
    961  * @param[in] ph handle of operation to cancel
    962  */
    963 static void
    964 oauth2_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
    965 {
    966   if (NULL != ph->ec)
    967   {
    968     TALER_JSON_external_conversion_stop (ph->ec);
    969     ph->ec = NULL;
    970   }
    971   if (NULL != ph->task)
    972   {
    973     GNUNET_SCHEDULER_cancel (ph->task);
    974     ph->task = NULL;
    975   }
    976   if (NULL != ph->job)
    977   {
    978     GNUNET_CURL_job_cancel (ph->job);
    979     ph->job = NULL;
    980   }
    981   if (NULL != ph->response)
    982   {
    983     MHD_destroy_response (ph->response);
    984     ph->response = NULL;
    985   }
    986   GNUNET_free (ph->provider_user_id);
    987   if (NULL != ph->attributes)
    988     json_decref (ph->attributes);
    989   GNUNET_free (ph->post_body);
    990   GNUNET_free (ph);
    991 }
    992 
    993 
    994 /**
    995  * Function called to asynchronously return the final
    996  * result to the callback.
    997  *
    998  * @param cls a `struct TALER_KYCLOGIC_ProofHandle`
    999  */
   1000 static void
   1001 return_proof_response (void *cls)
   1002 {
   1003   struct TALER_KYCLOGIC_ProofHandle *ph = cls;
   1004   const char *provider_name;
   1005 
   1006   ph->task = NULL;
   1007   provider_name = ph->pd->section;
   1008   if (0 !=
   1009       strncasecmp (provider_name,
   1010                    "KYC-PROVIDER-",
   1011                    strlen ("KYC-PROVIDER-")))
   1012   {
   1013     GNUNET_break (0);
   1014   }
   1015   else
   1016   {
   1017     provider_name += strlen ("KYC-PROVIDER-");
   1018   }
   1019   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1020               "Returning KYC proof from `%s'\n",
   1021               provider_name);
   1022   ph->cb (ph->cb_cls,
   1023           ph->status,
   1024           provider_name,
   1025           ph->provider_user_id,
   1026           ph->provider_legitimization_id,
   1027           GNUNET_TIME_relative_to_absolute (ph->pd->validity),
   1028           ph->attributes,
   1029           ph->http_status,
   1030           ph->response);
   1031   ph->response = NULL; /*Ownership passed to 'ph->cb'!*/
   1032   oauth2_proof_cancel (ph);
   1033 }
   1034 
   1035 
   1036 /**
   1037  * Load a @a template and substitute using @a root, returning the result in a
   1038  * @a reply encoded suitable for the @a connection with the given @a
   1039  * http_status code.  On errors, the @a http_status code
   1040  * is updated to reflect the type of error encoded in the
   1041  * @a reply.
   1042  *
   1043  * @param connection the connection we act upon
   1044  * @param[in,out] http_status code to use on success,
   1045  *           set to alternative code on failure
   1046  * @param template basename of the template to load
   1047  * @param root JSON object to pass as the root context
   1048  * @param[out] reply where to write the response object
   1049  * @return #GNUNET_OK on success (reply queued), #GNUNET_NO if an error was queued,
   1050  *         #GNUNET_SYSERR on failure (to queue an error)
   1051  */
   1052 static enum GNUNET_GenericReturnValue
   1053 templating_build (struct MHD_Connection *connection,
   1054                   unsigned int *http_status,
   1055                   const char *template,
   1056                   const json_t *root,
   1057                   struct MHD_Response **reply)
   1058 {
   1059   enum GNUNET_GenericReturnValue ret;
   1060 
   1061   ret = TALER_TEMPLATING_build (connection,
   1062                                 http_status,
   1063                                 template,
   1064                                 NULL,
   1065                                 NULL,
   1066                                 root,
   1067                                 reply);
   1068   if (GNUNET_SYSERR != ret)
   1069   {
   1070     GNUNET_break (MHD_NO !=
   1071                   MHD_add_response_header (*reply,
   1072                                            MHD_HTTP_HEADER_CONTENT_TYPE,
   1073                                            "text/html"));
   1074   }
   1075   return ret;
   1076 }
   1077 
   1078 
   1079 /**
   1080  * The request for @a ph failed. We may have gotten a useful error
   1081  * message in @a j. Generate a failure response.
   1082  *
   1083  * @param[in,out] ph request that failed
   1084  * @param j reply from the server (or NULL)
   1085  */
   1086 static void
   1087 handle_proof_error (struct TALER_KYCLOGIC_ProofHandle *ph,
   1088                     const json_t *j)
   1089 {
   1090   enum GNUNET_GenericReturnValue res;
   1091 
   1092   {
   1093     const char *msg;
   1094     const char *desc;
   1095     struct GNUNET_JSON_Specification spec[] = {
   1096       GNUNET_JSON_spec_string ("error",
   1097                                &msg),
   1098       GNUNET_JSON_spec_string ("error_description",
   1099                                &desc),
   1100       GNUNET_JSON_spec_end ()
   1101     };
   1102     const char *emsg;
   1103     unsigned int line;
   1104 
   1105     res = GNUNET_JSON_parse (j,
   1106                              spec,
   1107                              &emsg,
   1108                              &line);
   1109   }
   1110 
   1111   if (GNUNET_OK != res)
   1112   {
   1113     json_t *body;
   1114 
   1115     GNUNET_break_op (0);
   1116     ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1117     ph->http_status
   1118       = MHD_HTTP_BAD_GATEWAY;
   1119     body = GNUNET_JSON_PACK (
   1120       GNUNET_JSON_pack_allow_null (
   1121         GNUNET_JSON_pack_object_incref ("server_response",
   1122                                         (json_t *) j)),
   1123       GNUNET_JSON_pack_bool ("debug",
   1124                              ph->pd->debug_mode),
   1125       TALER_JSON_pack_ec (
   1126         TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1127     GNUNET_assert (NULL != body);
   1128     GNUNET_break (
   1129       GNUNET_SYSERR !=
   1130       templating_build (ph->connection,
   1131                         &ph->http_status,
   1132                         "oauth2-authorization-failure-malformed",
   1133                         body,
   1134                         &ph->response));
   1135     json_decref (body);
   1136     return;
   1137   }
   1138   ph->status = TALER_KYCLOGIC_STATUS_USER_ABORTED;
   1139   ph->http_status = MHD_HTTP_FORBIDDEN;
   1140   GNUNET_break (
   1141     GNUNET_SYSERR !=
   1142     templating_build (ph->connection,
   1143                       &ph->http_status,
   1144                       "oauth2-authorization-failure",
   1145                       j,
   1146                       &ph->response));
   1147 }
   1148 
   1149 
   1150 /**
   1151  * Type of a callback that receives a JSON @a result.
   1152  *
   1153  * @param cls closure with a `struct TALER_KYCLOGIC_ProofHandle *`
   1154  * @param status_type how did the process die
   1155  * @param code termination status code from the process
   1156  * @param attr result some JSON result, NULL if we failed to get an JSON output
   1157  */
   1158 static void
   1159 converted_proof_cb (void *cls,
   1160                     enum GNUNET_OS_ProcessStatusType status_type,
   1161                     unsigned long code,
   1162                     const json_t *attr)
   1163 {
   1164   struct TALER_KYCLOGIC_ProofHandle *ph = cls;
   1165   const struct TALER_KYCLOGIC_ProviderDetails *pd = ph->pd;
   1166 
   1167   ph->ec = NULL;
   1168   if ( (NULL == attr) ||
   1169        (GNUNET_OS_PROCESS_EXITED != status_type) ||
   1170        (0 != code) )
   1171   {
   1172     json_t *body;
   1173     char *msg;
   1174 
   1175     GNUNET_break_op (0);
   1176     ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1177     ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1178     if ( (GNUNET_OS_PROCESS_EXITED != status_type) ||
   1179          (0 != code) )
   1180       GNUNET_asprintf (&msg,
   1181                        "Attribute converter died with status %d/%ld",
   1182                        (int) status_type,
   1183                        code);
   1184     else
   1185       msg = GNUNET_strdup (
   1186         "Attribute converter response was not in JSON format");
   1187     body = GNUNET_JSON_PACK (
   1188       GNUNET_JSON_pack_string ("converter",
   1189                                pd->conversion_binary),
   1190       GNUNET_JSON_pack_allow_null (
   1191         GNUNET_JSON_pack_object_incref ("attributes",
   1192                                         (json_t *) attr)),
   1193       GNUNET_JSON_pack_bool ("debug",
   1194                              ph->pd->debug_mode),
   1195       GNUNET_JSON_pack_string ("message",
   1196                                msg),
   1197       TALER_JSON_pack_ec (
   1198         TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1199     GNUNET_free (msg);
   1200     GNUNET_break (
   1201       GNUNET_SYSERR !=
   1202       templating_build (ph->connection,
   1203                         &ph->http_status,
   1204                         "oauth2-conversion-failure",
   1205                         body,
   1206                         &ph->response));
   1207     json_decref (body);
   1208     ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1209                                          ph);
   1210     return;
   1211   }
   1212   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1213               "Attribute conversion output is:\n");
   1214 #if DEBUG
   1215   json_dumpf (attr,
   1216               stderr,
   1217               JSON_INDENT (2));
   1218   fprintf (stderr,
   1219            "\n");
   1220 #endif
   1221   {
   1222     const char *id;
   1223     struct GNUNET_JSON_Specification ispec[] = {
   1224       GNUNET_JSON_spec_string ("id",
   1225                                &id),
   1226       GNUNET_JSON_spec_end ()
   1227     };
   1228     enum GNUNET_GenericReturnValue res;
   1229     const char *emsg;
   1230     unsigned int line;
   1231 
   1232     res = GNUNET_JSON_parse (attr,
   1233                              ispec,
   1234                              &emsg,
   1235                              &line);
   1236     if (GNUNET_OK != res)
   1237     {
   1238       json_t *body;
   1239 
   1240       GNUNET_break_op (0);
   1241       ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1242       ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1243       body = GNUNET_JSON_PACK (
   1244         GNUNET_JSON_pack_string ("converter",
   1245                                  pd->conversion_binary),
   1246         GNUNET_JSON_pack_string ("message",
   1247                                  "Unexpected response from KYC attribute converter: returned JSON data must contain 'id' field"),
   1248         GNUNET_JSON_pack_bool ("debug",
   1249                                ph->pd->debug_mode),
   1250         GNUNET_JSON_pack_object_incref ("attributes",
   1251                                         (json_t *) attr),
   1252         TALER_JSON_pack_ec (
   1253           TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1254       GNUNET_break (
   1255         GNUNET_SYSERR !=
   1256         templating_build (ph->connection,
   1257                           &ph->http_status,
   1258                           "oauth2-conversion-failure",
   1259                           body,
   1260                           &ph->response));
   1261       json_decref (body);
   1262       ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1263                                            ph);
   1264       return;
   1265     }
   1266     ph->provider_user_id = GNUNET_strdup (id);
   1267   }
   1268   if (! json_is_string (json_object_get (attr,
   1269                                          "FORM_ID")))
   1270   {
   1271     json_t *body;
   1272 
   1273     GNUNET_break_op (0);
   1274     ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1275     ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1276     body = GNUNET_JSON_PACK (
   1277       GNUNET_JSON_pack_string ("converter",
   1278                                pd->conversion_binary),
   1279       GNUNET_JSON_pack_string ("message",
   1280                                "Missing 'FORM_ID' field in attributes"),
   1281       GNUNET_JSON_pack_bool ("debug",
   1282                              ph->pd->debug_mode),
   1283       GNUNET_JSON_pack_object_incref ("attributes",
   1284                                       (json_t *) attr),
   1285       TALER_JSON_pack_ec (
   1286         TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1287     GNUNET_break (
   1288       GNUNET_SYSERR !=
   1289       templating_build (ph->connection,
   1290                         &ph->http_status,
   1291                         "oauth2-conversion-failure",
   1292                         body,
   1293                         &ph->response));
   1294     json_decref (body);
   1295     ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1296                                          ph);
   1297     return;
   1298   }
   1299   ph->status = TALER_KYCLOGIC_STATUS_SUCCESS;
   1300   ph->response = MHD_create_response_from_buffer_static (0,
   1301                                                          "");
   1302   GNUNET_assert (NULL != ph->response);
   1303   GNUNET_break (MHD_YES ==
   1304                 MHD_add_response_header (
   1305                   ph->response,
   1306                   MHD_HTTP_HEADER_LOCATION,
   1307                   ph->pd->post_kyc_redirect_url));
   1308   ph->http_status = MHD_HTTP_SEE_OTHER;
   1309   ph->attributes = json_incref ((json_t *) attr);
   1310   ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1311                                        ph);
   1312 }
   1313 
   1314 
   1315 /**
   1316  * The request for @a ph succeeded (presumably).
   1317  * Call continuation with the result.
   1318  *
   1319  * @param[in,out] ph request that succeeded
   1320  * @param j reply from the server
   1321  */
   1322 static void
   1323 parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
   1324                            const json_t *j)
   1325 {
   1326   const struct TALER_KYCLOGIC_ProviderDetails *pd = ph->pd;
   1327   const char *argv[] = {
   1328     pd->conversion_binary,
   1329     NULL,
   1330   };
   1331 
   1332   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1333               "Calling converter `%s' with JSON\n",
   1334               pd->conversion_binary);
   1335 #if DEBUG
   1336   json_dumpf (j,
   1337               stderr,
   1338               JSON_INDENT (2));
   1339 #endif
   1340   ph->ec = TALER_JSON_external_conversion_start (
   1341     j,
   1342     &converted_proof_cb,
   1343     ph,
   1344     pd->conversion_binary,
   1345     argv);
   1346   if (NULL != ph->ec)
   1347     return;
   1348   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1349               "Failed to start OAUTH2 conversion helper `%s'\n",
   1350               pd->conversion_binary);
   1351   ph->status = TALER_KYCLOGIC_STATUS_INTERNAL_ERROR;
   1352   ph->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
   1353   {
   1354     json_t *body;
   1355 
   1356     body = GNUNET_JSON_PACK (
   1357       GNUNET_JSON_pack_string ("converter",
   1358                                pd->conversion_binary),
   1359       GNUNET_JSON_pack_bool ("debug",
   1360                              ph->pd->debug_mode),
   1361       GNUNET_JSON_pack_string ("message",
   1362                                "Failed to launch KYC conversion helper process."),
   1363       TALER_JSON_pack_ec (
   1364         TALER_EC_EXCHANGE_GENERIC_KYC_CONVERTER_FAILED));
   1365     GNUNET_break (
   1366       GNUNET_SYSERR !=
   1367       templating_build (ph->connection,
   1368                         &ph->http_status,
   1369                         "oauth2-conversion-failure",
   1370                         body,
   1371                         &ph->response));
   1372     json_decref (body);
   1373   }
   1374   ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1375                                        ph);
   1376 }
   1377 
   1378 
   1379 /**
   1380  * After we are done with the CURL interaction we
   1381  * need to update our database state with the information
   1382  * retrieved.
   1383  *
   1384  * @param cls our `struct TALER_KYCLOGIC_ProofHandle`
   1385  * @param response_code HTTP response code from server, 0 on hard error
   1386  * @param response in JSON, NULL if response was not in JSON format
   1387  */
   1388 static void
   1389 handle_curl_proof_finished (void *cls,
   1390                             long response_code,
   1391                             const void *response)
   1392 {
   1393   struct TALER_KYCLOGIC_ProofHandle *ph = cls;
   1394   const json_t *j = response;
   1395 
   1396   ph->job = NULL;
   1397   switch (response_code)
   1398   {
   1399   case 0:
   1400     {
   1401       json_t *body;
   1402 
   1403       ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1404       ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1405 
   1406       body = GNUNET_JSON_PACK (
   1407         GNUNET_JSON_pack_string ("message",
   1408                                  "No response from KYC gateway"),
   1409         TALER_JSON_pack_ec (
   1410           TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1411       GNUNET_break (
   1412         GNUNET_SYSERR !=
   1413         templating_build (ph->connection,
   1414                           &ph->http_status,
   1415                           "oauth2-provider-failure",
   1416                           body,
   1417                           &ph->response));
   1418       json_decref (body);
   1419     }
   1420     break;
   1421   case MHD_HTTP_OK:
   1422     parse_proof_success_reply (ph,
   1423                                j);
   1424     return;
   1425   default:
   1426     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1427                 "OAuth2.0 info URL returned HTTP status %u\n",
   1428                 (unsigned int) response_code);
   1429     handle_proof_error (ph,
   1430                         j);
   1431     break;
   1432   }
   1433   ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1434                                        ph);
   1435 }
   1436 
   1437 
   1438 /**
   1439  * After we are done with the CURL interaction we
   1440  * need to fetch the user's account details.
   1441  *
   1442  * @param cls our `struct KycProofContext`
   1443  * @param response_code HTTP response code from server, 0 on hard error
   1444  * @param response in JSON, NULL if response was not in JSON format
   1445  */
   1446 static void
   1447 handle_curl_login_finished (void *cls,
   1448                             long response_code,
   1449                             const void *response)
   1450 {
   1451   struct TALER_KYCLOGIC_ProofHandle *ph = cls;
   1452   const json_t *j = response;
   1453 
   1454   ph->job = NULL;
   1455   switch (response_code)
   1456   {
   1457   case MHD_HTTP_OK:
   1458     {
   1459       const char *access_token;
   1460       const char *token_type;
   1461       uint64_t expires_in_s;
   1462       const char *refresh_token;
   1463       bool no_expires;
   1464       bool no_refresh;
   1465       struct GNUNET_JSON_Specification spec[] = {
   1466         GNUNET_JSON_spec_string ("access_token",
   1467                                  &access_token),
   1468         GNUNET_JSON_spec_string ("token_type",
   1469                                  &token_type),
   1470         GNUNET_JSON_spec_mark_optional (
   1471           GNUNET_JSON_spec_uint64 ("expires_in",
   1472                                    &expires_in_s),
   1473           &no_expires),
   1474         GNUNET_JSON_spec_mark_optional (
   1475           GNUNET_JSON_spec_string ("refresh_token",
   1476                                    &refresh_token),
   1477           &no_refresh),
   1478         GNUNET_JSON_spec_end ()
   1479       };
   1480       CURL *eh;
   1481 
   1482       {
   1483         enum GNUNET_GenericReturnValue res;
   1484         const char *emsg;
   1485         unsigned int line;
   1486 
   1487         res = GNUNET_JSON_parse (j,
   1488                                  spec,
   1489                                  &emsg,
   1490                                  &line);
   1491         if (GNUNET_OK != res)
   1492         {
   1493           json_t *body;
   1494 
   1495           GNUNET_break_op (0);
   1496           ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1497           ph->http_status
   1498             = MHD_HTTP_BAD_GATEWAY;
   1499           body = GNUNET_JSON_PACK (
   1500             GNUNET_JSON_pack_object_incref ("server_response",
   1501                                             (json_t *) j),
   1502             GNUNET_JSON_pack_bool ("debug",
   1503                                    ph->pd->debug_mode),
   1504             GNUNET_JSON_pack_string ("message",
   1505                                      "Unexpected response from KYC gateway: required fields missing or malformed"),
   1506             TALER_JSON_pack_ec (
   1507               TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1508           GNUNET_break (
   1509             GNUNET_SYSERR !=
   1510             templating_build (ph->connection,
   1511                               &ph->http_status,
   1512                               "oauth2-provider-failure",
   1513                               body,
   1514                               &ph->response));
   1515           json_decref (body);
   1516           break;
   1517         }
   1518       }
   1519       if (0 != strcasecmp (token_type,
   1520                            "bearer"))
   1521       {
   1522         json_t *body;
   1523 
   1524         GNUNET_break_op (0);
   1525         ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1526         ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1527         body = GNUNET_JSON_PACK (
   1528           GNUNET_JSON_pack_object_incref ("server_response",
   1529                                           (json_t *) j),
   1530           GNUNET_JSON_pack_bool ("debug",
   1531                                  ph->pd->debug_mode),
   1532           GNUNET_JSON_pack_string ("message",
   1533                                    "Unexpected 'token_type' in response from KYC gateway: 'bearer' token required"),
   1534           TALER_JSON_pack_ec (
   1535             TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1536         GNUNET_break (
   1537           GNUNET_SYSERR !=
   1538           templating_build (ph->connection,
   1539                             &ph->http_status,
   1540                             "oauth2-provider-failure",
   1541                             body,
   1542                             &ph->response));
   1543         json_decref (body);
   1544         break;
   1545       }
   1546 
   1547       /* We guard against a few characters that could
   1548          conceivably be abused to mess with the HTTP header */
   1549       if ( (NULL != strchr (access_token,
   1550                             '\n')) ||
   1551            (NULL != strchr (access_token,
   1552                             '\r')) ||
   1553            (NULL != strchr (access_token,
   1554                             ' ')) ||
   1555            (NULL != strchr (access_token,
   1556                             ';')) )
   1557       {
   1558         json_t *body;
   1559 
   1560         GNUNET_break_op (0);
   1561         ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1562         ph->http_status = MHD_HTTP_BAD_GATEWAY;
   1563         body = GNUNET_JSON_PACK (
   1564           GNUNET_JSON_pack_object_incref ("server_response",
   1565                                           (json_t *) j),
   1566           GNUNET_JSON_pack_bool ("debug",
   1567                                  ph->pd->debug_mode),
   1568           GNUNET_JSON_pack_string ("message",
   1569                                    "Illegal character in access token"),
   1570           TALER_JSON_pack_ec (
   1571             TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
   1572         GNUNET_break (
   1573           GNUNET_SYSERR !=
   1574           templating_build (ph->connection,
   1575                             &ph->http_status,
   1576                             "oauth2-provider-failure",
   1577                             body,
   1578                             &ph->response));
   1579         json_decref (body);
   1580         break;
   1581       }
   1582 
   1583       eh = curl_easy_init ();
   1584       GNUNET_assert (NULL != eh);
   1585       GNUNET_assert (CURLE_OK ==
   1586                      curl_easy_setopt (eh,
   1587                                        CURLOPT_URL,
   1588                                        ph->pd->info_url));
   1589       {
   1590         char *hdr;
   1591         struct curl_slist *slist;
   1592 
   1593         GNUNET_asprintf (&hdr,
   1594                          "%s: Bearer %s",
   1595                          MHD_HTTP_HEADER_AUTHORIZATION,
   1596                          access_token);
   1597         slist = curl_slist_append (NULL,
   1598                                    hdr);
   1599         ph->job = GNUNET_CURL_job_add2 (ph->pd->ps->curl_ctx,
   1600                                         eh,
   1601                                         slist,
   1602                                         &handle_curl_proof_finished,
   1603                                         ph);
   1604         curl_slist_free_all (slist);
   1605         GNUNET_free (hdr);
   1606       }
   1607       return;
   1608     }
   1609   default:
   1610     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1611                 "OAuth2.0 login URL returned HTTP status %u\n",
   1612                 (unsigned int) response_code);
   1613     handle_proof_error (ph,
   1614                         j);
   1615     break;
   1616   }
   1617   return_proof_response (ph);
   1618 }
   1619 
   1620 
   1621 /**
   1622  * Check KYC status and return status to human.
   1623  *
   1624  * @param cls the @e cls of this struct with the plugin-specific state
   1625  * @param pd provider configuration details
   1626  * @param connection MHD connection object (for HTTP headers)
   1627  * @param account_id which account to trigger process for
   1628  * @param process_row row in the legitimization processes table the legitimization is for
   1629  * @param provider_user_id user ID (or NULL) the proof is for
   1630  * @param provider_legitimization_id legitimization ID the proof is for
   1631  * @param cb function to call with the result
   1632  * @param cb_cls closure for @a cb
   1633  * @return handle to cancel operation early
   1634  */
   1635 static struct TALER_KYCLOGIC_ProofHandle *
   1636 oauth2_proof (void *cls,
   1637               const struct TALER_KYCLOGIC_ProviderDetails *pd,
   1638               struct MHD_Connection *connection,
   1639               const struct TALER_NormalizedPaytoHashP *account_id,
   1640               uint64_t process_row,
   1641               const char *provider_user_id,
   1642               const char *provider_legitimization_id,
   1643               TALER_KYCLOGIC_ProofCallback cb,
   1644               void *cb_cls)
   1645 {
   1646   struct PluginState *ps = cls;
   1647   struct TALER_KYCLOGIC_ProofHandle *ph;
   1648   const char *code;
   1649 
   1650   GNUNET_break (NULL == provider_user_id);
   1651   ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
   1652   GNUNET_snprintf (ph->provider_legitimization_id,
   1653                    sizeof (ph->provider_legitimization_id),
   1654                    "%llu",
   1655                    (unsigned long long) process_row);
   1656   if ( (NULL != provider_legitimization_id) &&
   1657        (0 != strcmp (provider_legitimization_id,
   1658                      ph->provider_legitimization_id)))
   1659   {
   1660     GNUNET_break (0);
   1661     GNUNET_free (ph);
   1662     return NULL;
   1663   }
   1664 
   1665   ph->pd = pd;
   1666   ph->connection = connection;
   1667   ph->h_payto = *account_id;
   1668   ph->cb = cb;
   1669   ph->cb_cls = cb_cls;
   1670   code = MHD_lookup_connection_value (connection,
   1671                                       MHD_GET_ARGUMENT_KIND,
   1672                                       "code");
   1673   if (NULL == code)
   1674   {
   1675     const char *err;
   1676     const char *desc;
   1677     const char *euri;
   1678     json_t *body;
   1679 
   1680     err = MHD_lookup_connection_value (connection,
   1681                                        MHD_GET_ARGUMENT_KIND,
   1682                                        "error");
   1683     if (NULL == err)
   1684     {
   1685       GNUNET_break_op (0);
   1686       ph->status = TALER_KYCLOGIC_STATUS_USER_PENDING;
   1687       ph->http_status = MHD_HTTP_BAD_REQUEST;
   1688       body = GNUNET_JSON_PACK (
   1689         GNUNET_JSON_pack_string ("message",
   1690                                  "'code' parameter malformed"),
   1691         TALER_JSON_pack_ec (
   1692           TALER_EC_GENERIC_PARAMETER_MALFORMED));
   1693       GNUNET_break (
   1694         GNUNET_SYSERR !=
   1695         templating_build (ph->connection,
   1696                           &ph->http_status,
   1697                           "oauth2-bad-request",
   1698                           body,
   1699                           &ph->response));
   1700       json_decref (body);
   1701       ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1702                                            ph);
   1703       return ph;
   1704     }
   1705     desc = MHD_lookup_connection_value (connection,
   1706                                         MHD_GET_ARGUMENT_KIND,
   1707                                         "error_description");
   1708     euri = MHD_lookup_connection_value (connection,
   1709                                         MHD_GET_ARGUMENT_KIND,
   1710                                         "error_uri");
   1711     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1712                 "OAuth2 process %llu failed with error `%s'\n",
   1713                 (unsigned long long) process_row,
   1714                 err);
   1715     if (0 == strcasecmp (err,
   1716                          "server_error"))
   1717       ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
   1718     else if (0 == strcasecmp (err,
   1719                               "unauthorized_client"))
   1720       ph->status = TALER_KYCLOGIC_STATUS_FAILED;
   1721     else if (0 == strcasecmp (err,
   1722                               "temporarily_unavailable"))
   1723       ph->status = TALER_KYCLOGIC_STATUS_PENDING;
   1724     else
   1725       ph->status = TALER_KYCLOGIC_STATUS_INTERNAL_ERROR;
   1726     ph->http_status = MHD_HTTP_FORBIDDEN;
   1727     body = GNUNET_JSON_PACK (
   1728       GNUNET_JSON_pack_string ("error",
   1729                                err),
   1730       GNUNET_JSON_pack_allow_null (
   1731         GNUNET_JSON_pack_string ("error_details",
   1732                                  desc)),
   1733       GNUNET_JSON_pack_allow_null (
   1734         GNUNET_JSON_pack_string ("error_uri",
   1735                                  euri)));
   1736     GNUNET_break (
   1737       GNUNET_SYSERR !=
   1738       templating_build (ph->connection,
   1739                         &ph->http_status,
   1740                         "oauth2-authentication-failure",
   1741                         body,
   1742                         &ph->response));
   1743     json_decref (body);
   1744     ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
   1745                                          ph);
   1746     return ph;
   1747 
   1748   }
   1749 
   1750   ph->eh = curl_easy_init ();
   1751   GNUNET_assert (NULL != ph->eh);
   1752   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1753               "Requesting OAuth 2.0 data via HTTP POST `%s'\n",
   1754               pd->token_url);
   1755   GNUNET_assert (CURLE_OK ==
   1756                  curl_easy_setopt (ph->eh,
   1757                                    CURLOPT_URL,
   1758                                    pd->token_url));
   1759 #if DEBUG
   1760   GNUNET_assert (CURLE_OK ==
   1761                  curl_easy_setopt (ph->eh,
   1762                                    CURLOPT_VERBOSE,
   1763                                    1));
   1764 #endif
   1765   GNUNET_assert (CURLE_OK ==
   1766                  curl_easy_setopt (ph->eh,
   1767                                    CURLOPT_POST,
   1768                                    1));
   1769   {
   1770     char *client_id;
   1771     char *client_secret;
   1772     char *authorization_code;
   1773     char *redirect_uri_encoded;
   1774     char *hps;
   1775 
   1776     hps = GNUNET_STRINGS_data_to_string_alloc (&ph->h_payto,
   1777                                                sizeof (ph->h_payto));
   1778     {
   1779       char *redirect_uri;
   1780 
   1781       GNUNET_asprintf (&redirect_uri,
   1782                        "%skyc-proof/%s",
   1783                        ps->exchange_base_url,
   1784                        &pd->section[strlen ("kyc-provider-")]);
   1785       redirect_uri_encoded = TALER_urlencode (redirect_uri);
   1786       GNUNET_free (redirect_uri);
   1787     }
   1788     GNUNET_assert (NULL != redirect_uri_encoded);
   1789     client_id = curl_easy_escape (ph->eh,
   1790                                   pd->client_id,
   1791                                   0);
   1792     GNUNET_assert (NULL != client_id);
   1793     client_secret = curl_easy_escape (ph->eh,
   1794                                       pd->client_secret,
   1795                                       0);
   1796     GNUNET_assert (NULL != client_secret);
   1797     authorization_code = curl_easy_escape (ph->eh,
   1798                                            code,
   1799                                            0);
   1800     GNUNET_assert (NULL != authorization_code);
   1801     GNUNET_asprintf (&ph->post_body,
   1802                      "client_id=%s&redirect_uri=%s&state=%s&client_secret=%s&code=%s&grant_type=authorization_code",
   1803                      client_id,
   1804                      redirect_uri_encoded,
   1805                      hps,
   1806                      client_secret,
   1807                      authorization_code);
   1808     curl_free (authorization_code);
   1809     curl_free (client_secret);
   1810     GNUNET_free (redirect_uri_encoded);
   1811     GNUNET_free (hps);
   1812     curl_free (client_id);
   1813   }
   1814   GNUNET_assert (CURLE_OK ==
   1815                  curl_easy_setopt (ph->eh,
   1816                                    CURLOPT_POSTFIELDS,
   1817                                    ph->post_body));
   1818   GNUNET_assert (CURLE_OK ==
   1819                  curl_easy_setopt (ph->eh,
   1820                                    CURLOPT_FOLLOWLOCATION,
   1821                                    1L));
   1822   /* limit MAXREDIRS to 5 as a simple security measure against
   1823      a potential infinite loop caused by a malicious target */
   1824   GNUNET_assert (CURLE_OK ==
   1825                  curl_easy_setopt (ph->eh,
   1826                                    CURLOPT_MAXREDIRS,
   1827                                    5L));
   1828 
   1829   ph->job = GNUNET_CURL_job_add (ps->curl_ctx,
   1830                                  ph->eh,
   1831                                  &handle_curl_login_finished,
   1832                                  ph);
   1833   return ph;
   1834 }
   1835 
   1836 
   1837 /**
   1838  * Function to asynchronously return the 404 not found
   1839  * page for the webhook.
   1840  *
   1841  * @param cls the `struct TALER_KYCLOGIC_WebhookHandle *`
   1842  */
   1843 static void
   1844 wh_return_not_found (void *cls)
   1845 {
   1846   struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
   1847   struct MHD_Response *response;
   1848 
   1849   wh->task = NULL;
   1850   response = MHD_create_response_from_buffer_static (0,
   1851                                                      "");
   1852   wh->cb (wh->cb_cls,
   1853           0LLU,
   1854           NULL,
   1855           false,
   1856           NULL,
   1857           NULL,
   1858           NULL,
   1859           TALER_KYCLOGIC_STATUS_KEEP,
   1860           GNUNET_TIME_UNIT_ZERO_ABS,
   1861           NULL,
   1862           MHD_HTTP_NOT_FOUND,
   1863           response);
   1864   GNUNET_free (wh);
   1865 }
   1866 
   1867 
   1868 /**
   1869  * Check KYC status and return result for Webhook.
   1870  *
   1871  * @param cls the @e cls of this struct with the plugin-specific state
   1872  * @param pd provider configuration details
   1873  * @param plc callback to lookup accounts with
   1874  * @param plc_cls closure for @a plc
   1875  * @param http_method HTTP method used for the webhook
   1876  * @param url_path rest of the URL after `/kyc-webhook/$LOGIC/`, as NULL-terminated array
   1877  * @param connection MHD connection object (for HTTP headers)
   1878  * @param body HTTP request body, or NULL if not available
   1879  * @param cb function to call with the result
   1880  * @param cb_cls closure for @a cb
   1881  * @return handle to cancel operation early
   1882  */
   1883 static struct TALER_KYCLOGIC_WebhookHandle *
   1884 oauth2_webhook (void *cls,
   1885                 const struct TALER_KYCLOGIC_ProviderDetails *pd,
   1886                 TALER_KYCLOGIC_ProviderLookupCallback plc,
   1887                 void *plc_cls,
   1888                 const char *http_method,
   1889                 const char *const url_path[],
   1890                 struct MHD_Connection *connection,
   1891                 const json_t *body,
   1892                 TALER_KYCLOGIC_WebhookCallback cb,
   1893                 void *cb_cls)
   1894 {
   1895   struct PluginState *ps = cls;
   1896   struct TALER_KYCLOGIC_WebhookHandle *wh;
   1897 
   1898   (void) pd;
   1899   (void) plc;
   1900   (void) plc_cls;
   1901   (void) http_method;
   1902   (void) url_path;
   1903   (void) connection;
   1904   (void) body;
   1905   GNUNET_break_op (0);
   1906   wh = GNUNET_new (struct TALER_KYCLOGIC_WebhookHandle);
   1907   wh->cb = cb;
   1908   wh->cb_cls = cb_cls;
   1909   wh->ps = ps;
   1910   wh->task = GNUNET_SCHEDULER_add_now (&wh_return_not_found,
   1911                                        wh);
   1912   return wh;
   1913 }
   1914 
   1915 
   1916 /**
   1917  * Cancel KYC webhook execution.
   1918  *
   1919  * @param[in] wh handle of operation to cancel
   1920  */
   1921 static void
   1922 oauth2_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
   1923 {
   1924   GNUNET_SCHEDULER_cancel (wh->task);
   1925   GNUNET_free (wh);
   1926 }
   1927 
   1928 
   1929 /**
   1930  * Initialize OAuth2.0 KYC logic plugin
   1931  *
   1932  * @param cls a configuration instance
   1933  * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin`
   1934  */
   1935 void *
   1936 libtaler_plugin_kyclogic_oauth2_init (void *cls);
   1937 
   1938 /* declaration to avoid compiler warning */
   1939 void *
   1940 libtaler_plugin_kyclogic_oauth2_init (void *cls)
   1941 {
   1942   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   1943   struct TALER_KYCLOGIC_Plugin *plugin;
   1944   struct PluginState *ps;
   1945 
   1946   ps = GNUNET_new (struct PluginState);
   1947   ps->cfg = cfg;
   1948   if (GNUNET_OK !=
   1949       GNUNET_CONFIGURATION_get_value_string (cfg,
   1950                                              "exchange",
   1951                                              "BASE_URL",
   1952                                              &ps->exchange_base_url))
   1953   {
   1954     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1955                                "exchange",
   1956                                "BASE_URL");
   1957     GNUNET_free (ps);
   1958     return NULL;
   1959   }
   1960   ps->curl_ctx
   1961     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1962                         &ps->curl_rc);
   1963   if (NULL == ps->curl_ctx)
   1964   {
   1965     GNUNET_break (0);
   1966     GNUNET_free (ps->exchange_base_url);
   1967     GNUNET_free (ps);
   1968     return NULL;
   1969   }
   1970   ps->curl_rc = GNUNET_CURL_gnunet_rc_create (ps->curl_ctx);
   1971 
   1972   plugin = GNUNET_new (struct TALER_KYCLOGIC_Plugin);
   1973   plugin->cls = ps;
   1974   plugin->load_configuration
   1975     = &oauth2_load_configuration;
   1976   plugin->unload_configuration
   1977     = &oauth2_unload_configuration;
   1978   plugin->initiate
   1979     = &oauth2_initiate;
   1980   plugin->initiate_get_expiration
   1981     = &oauth2_initiate_get_expiration;
   1982   plugin->initiate_cancel
   1983     = &oauth2_initiate_cancel;
   1984   plugin->proof
   1985     = &oauth2_proof;
   1986   plugin->proof_cancel
   1987     = &oauth2_proof_cancel;
   1988   plugin->webhook
   1989     = &oauth2_webhook;
   1990   plugin->webhook_cancel
   1991     = &oauth2_webhook_cancel;
   1992   return plugin;
   1993 }
   1994 
   1995 
   1996 /**
   1997  * Unload authorization plugin
   1998  *
   1999  * @param cls a `struct TALER_KYCLOGIC_Plugin`
   2000  * @return NULL (always)
   2001  */
   2002 void *
   2003 libtaler_plugin_kyclogic_oauth2_done (void *cls);
   2004 
   2005 /* declaration to avoid compiler warning */
   2006 void *
   2007 libtaler_plugin_kyclogic_oauth2_done (void *cls)
   2008 {
   2009   struct TALER_KYCLOGIC_Plugin *plugin = cls;
   2010   struct PluginState *ps = plugin->cls;
   2011 
   2012   if (NULL != ps->curl_ctx)
   2013   {
   2014     GNUNET_CURL_fini (ps->curl_ctx);
   2015     ps->curl_ctx = NULL;
   2016   }
   2017   if (NULL != ps->curl_rc)
   2018   {
   2019     GNUNET_CURL_gnunet_rc_destroy (ps->curl_rc);
   2020     ps->curl_rc = NULL;
   2021   }
   2022   GNUNET_free (ps->exchange_base_url);
   2023   GNUNET_free (ps);
   2024   GNUNET_free (plugin);
   2025   return NULL;
   2026 }