LCOV - code coverage report
Current view: top level - kyclogic - plugin_kyclogic_oauth2.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 54.9 % 546 300
Test Date: 2026-01-09 13:26:54 Functions: 80.0 % 20 16

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

Generated by: LCOV version 2.0-1