LCOV - code coverage report
Current view: top level - kyclogic - plugin_kyclogic_oauth2.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 54.5 % 569 310
Test Date: 2026-09-09 15:11:34 Functions: 81.8 % 22 18

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

Generated by: LCOV version 2.0-1