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 : }
|