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