From b2a67fcff9b5a7e056b2acd6a3a8343365ab35c0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 17 Aug 2022 14:36:16 +0200 Subject: -fix kycaid logic issues --- src/kyclogic/kyclogic_api.c | 3 + src/kyclogic/plugin_kyclogic_kycaid.c | 110 ++++++++++++++++++++++++------- src/kyclogic/taler-exchange-kyc-tester.c | 47 +++++++++---- 3 files changed, 123 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index 2b4ce0d3b..18707a18f 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -748,6 +748,9 @@ TALER_KYCLOGIC_kyc_done (void) { struct TALER_KYCLOGIC_KycCheck *kc = kyc_checks[i]; + GNUNET_array_grow (kc->providers, + kc->num_providers, + 0); GNUNET_free (kc->name); GNUNET_free (kc); } diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c index 933c0d353..449dac51b 100644 --- a/src/kyclogic/plugin_kyclogic_kycaid.c +++ b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -720,10 +720,54 @@ kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh) } GNUNET_free (wh->verification_id); GNUNET_free (wh->applicant_id); + GNUNET_free (wh->url); GNUNET_free (wh); } +/** + * Extract KYC failure reasons and log those + * + * @param verifications JSON object with failure details + */ +static void +log_failure (json_t *verifications) +{ + json_t *member; + const char *name; + json_object_foreach (verifications, name, member) + { + bool iverified; + const char *comment; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("verified", + &iverified), + GNUNET_JSON_spec_string ("comment", + &comment), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (member, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + json_dumpf (member, + stderr, + JSON_INDENT (2)); + continue; + } + if (iverified) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC verification of attribute `%s' failed: %s\n", + name, + comment); + } +} + + /** * Function called when we're done processing the * HTTP "/verifications/{verification_id}" request. @@ -749,7 +793,6 @@ handle_webhook_finished (void *cls, const char *applicant_id; const char *verification_id; const char *status; - const char *type; bool verified; json_t *verifications; struct GNUNET_JSON_Specification spec[] = { @@ -757,10 +800,8 @@ handle_webhook_finished (void *cls, &applicant_id), GNUNET_JSON_spec_string ("verification_id", &verification_id), - GNUNET_JSON_spec_string ("type", - &type), GNUNET_JSON_spec_string ("status", - &status), + &status), /* completed, pending, ... */ GNUNET_JSON_spec_bool ("verified", &verified), GNUNET_JSON_spec_json ("verifications", @@ -794,12 +835,10 @@ handle_webhook_finished (void *cls, resp); break; } - /* FIXME: comment out, unless debugging ... */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "The provider returned the following verifications:\n"); - json_dumpf (verifications, - stderr, - JSON_INDENT (2)); + if (! verified) + { + log_failure (verifications); + } resp = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT); @@ -980,11 +1019,15 @@ async_webhook_reply (void *cls) { struct TALER_KYCLOGIC_WebhookHandle *wh = cls; + fprintf (stderr, + "async reply\n"); wh->cb (wh->cb_cls, - 0LLU, /* legitimization row ID (unknown) */ - NULL, /* our account ID */ - NULL, /* provider user ID */ - NULL, /* provider legi ID */ + wh->legi_row, + (0 == wh->legi_row) + ? NULL + : &wh->h_payto, + wh->applicant_id, /* provider user ID */ + wh->verification_id, /* provider legi ID */ TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ wh->response_code, @@ -994,7 +1037,11 @@ async_webhook_reply (void *cls) /** - * Check KYC status and return result for Webhook. + * Check KYC status and return result for Webhook. We do NOT implement the + * authentication check proposed by the KYCAID documentation, as it would + * allow an attacker who learns the access token to easily bypass the KYC + * checks. Instead, we insist on explicitly requesting the KYC status from the + * provider (at least on success). * * @param cls the @e cls of this struct with the plugin-specific state * @param pd provider configuration details @@ -1085,6 +1132,7 @@ kycaid_webhook (void *cls, wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, wh); + GNUNET_JSON_parse_free (spec); return wh; } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) @@ -1098,10 +1146,25 @@ kycaid_webhook (void *cls, wh->response_code = MHD_HTTP_NOT_FOUND; wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, wh); + GNUNET_JSON_parse_free (spec); return wh; } wh->verification_id = GNUNET_strdup (verification_id); wh->applicant_id = GNUNET_strdup (applicant_id); + if (! verified) + { + /* We don't need to re-confirm the failure by + asking the API again. */ + log_failure (verifications); + wh->response_code = MHD_HTTP_NO_CONTENT; + wh->resp = MHD_create_response_from_buffer (0, + "", + MHD_RESPMEM_PERSISTENT); + wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, + wh); + GNUNET_JSON_parse_free (spec); + return wh; + } eh = curl_easy_init (); if (NULL == eh) @@ -1113,6 +1176,7 @@ kycaid_webhook (void *cls, wh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; wh->task = GNUNET_SCHEDULER_add_now (&async_webhook_reply, wh); + GNUNET_JSON_parse_free (spec); return wh; } @@ -1122,7 +1186,7 @@ kycaid_webhook (void *cls, GNUNET_break (CURLE_OK == curl_easy_setopt (eh, CURLOPT_VERBOSE, - 1)); + 0)); GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_MAXREDIRS, @@ -1131,18 +1195,18 @@ kycaid_webhook (void *cls, curl_easy_setopt (eh, CURLOPT_URL, wh->url)); - wh->job = GNUNET_CURL_job_add (ps->curl_ctx, - eh, - &handle_webhook_finished, - wh); - GNUNET_CURL_extend_headers (wh->job, - pd->slist); + wh->job = GNUNET_CURL_job_add2 (ps->curl_ctx, + eh, + pd->slist, + &handle_webhook_finished, + wh); + GNUNET_JSON_parse_free (spec); return wh; } /** - * Initialize Kycaid.0 KYC logic plugin + * Initialize kycaid logic plugin * * @param cls a configuration instance * @return NULL on error, otherwise a `struct TALER_KYCLOGIC_Plugin` diff --git a/src/kyclogic/taler-exchange-kyc-tester.c b/src/kyclogic/taler-exchange-kyc-tester.c index 2f3fa561c..40aadba1a 100644 --- a/src/kyclogic/taler-exchange-kyc-tester.c +++ b/src/kyclogic/taler-exchange-kyc-tester.c @@ -81,6 +81,11 @@ struct TEKT_RequestContext */ void *rh_ctx; + /** + * Uploaded JSON body, if any. + */ + json_t *root; + /** * HTTP status to return upon resume if @e response * is non-NULL. @@ -555,6 +560,8 @@ handler_kyc_webhook_generic ( TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN, "$LOGIC"); } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Calling KYC provider specific webhook\n"); kwh->wh = kwh->plugin->webhook (kwh->plugin->cls, kwh->pd, &kyc_provider_account_lookup, @@ -583,6 +590,8 @@ handler_kyc_webhook_generic ( if (NULL != kwh->response) { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Returning queued reply for KWH\n"); /* handle _failed_ resumed cases */ return MHD_queue_response (rc->connection, kwh->response_code, @@ -845,7 +854,6 @@ proceed_with_handler (struct TEKT_RequestContext *rc, const struct TEKT_RequestHandler *rh = rc->rh; const char *args[rh->nargs + 2]; size_t ulen = strlen (url) + 1; - json_t *root = NULL; MHD_RESULT ret; /* We do check for "ulen" here, because we'll later stack-allocate a buffer @@ -866,8 +874,9 @@ proceed_with_handler (struct TEKT_RequestContext *rc, /* All POST endpoints come with a body in JSON format. So we parse the JSON here. */ - if (0 == strcasecmp (rh->method, - MHD_HTTP_METHOD_POST)) + if ( (NULL == rc->root) && + (0 == strcasecmp (rh->method, + MHD_HTTP_METHOD_POST)) ) { enum GNUNET_GenericReturnValue res; @@ -875,16 +884,17 @@ proceed_with_handler (struct TEKT_RequestContext *rc, &rc->opaque_post_parsing_context, upload_data, upload_data_size, - &root); + &rc->root); if (GNUNET_SYSERR == res) { - GNUNET_assert (NULL == root); + GNUNET_assert (NULL == rc->root); + GNUNET_break (0); return MHD_NO; /* bad upload, could not even generate error */ } if ( (GNUNET_NO == res) || - (NULL == root) ) + (NULL == rc->root) ) { - GNUNET_assert (NULL == root); + GNUNET_assert (NULL == rc->root); return MHD_YES; /* so far incomplete upload or parser error */ } } @@ -921,7 +931,6 @@ proceed_with_handler (struct TEKT_RequestContext *rc, rh->url, url); GNUNET_break_op (0); - json_decref (root); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_NOT_FOUND, TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, @@ -931,16 +940,15 @@ proceed_with_handler (struct TEKT_RequestContext *rc, /* Above logic ensures that 'root' is exactly non-NULL for POST operations, so we test for 'root' to decide which handler to invoke. */ - if (NULL != root) + if (NULL != rc->root) ret = rh->handler.post (rc, - root, + rc->root, args); else /* We also only have "POST" or "GET" in the API for at this point (OPTIONS/HEAD are taken care of earlier) */ ret = rh->handler.get (rc, args); } - json_decref (root); return ret; } @@ -949,8 +957,15 @@ static void rh_cleaner_cb (struct TEKT_RequestContext *rc) { if (NULL != rc->response) + { MHD_destroy_response (rc->response); - GNUNET_free (rc); + rc->response = NULL; + } + if (NULL != rc->root) + { + json_decref (rc->root); + rc->root = NULL; + } } @@ -1082,7 +1097,8 @@ handle_mhd_request (void *cls, continue; found = true; /* The URL is a match! What we now do depends on the method. */ - if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS)) + if (0 == strcasecmp (method, + MHD_HTTP_METHOD_OPTIONS)) { return TALER_MHD_reply_cors_preflight (connection); } @@ -1276,7 +1292,7 @@ initiate_cb ( return; } fprintf (stdout, - "Visit `%s' to begin KYC process (-u: '%s', -l: '%s')\n", + "Visit `%s' to begin KYC process (-u: '%s', -U: '%s')\n", redirect_url, provider_user_id, provider_legitimization_id); @@ -1393,6 +1409,9 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Starting daemon on port %u\n", + (unsigned int) serve_port); mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG | MHD_USE_DUAL_STACK -- cgit v1.2.3