Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021-2022 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. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_kyc-check.c
18 : * @brief Handle request for generic KYC check.
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include <pthread.h>
27 : #include "taler_json_lib.h"
28 : #include "taler_kyclogic_lib.h"
29 : #include "taler_mhd_lib.h"
30 : #include "taler_signatures.h"
31 : #include "taler_dbevents.h"
32 : #include "taler-exchange-httpd_keys.h"
33 : #include "taler-exchange-httpd_kyc-wallet.h"
34 : #include "taler-exchange-httpd_responses.h"
35 :
36 :
37 : /**
38 : * Reserve GET request that is long-polling.
39 : */
40 : struct KycPoller
41 : {
42 : /**
43 : * Kept in a DLL.
44 : */
45 : struct KycPoller *next;
46 :
47 : /**
48 : * Kept in a DLL.
49 : */
50 : struct KycPoller *prev;
51 :
52 : /**
53 : * Connection we are handling.
54 : */
55 : struct MHD_Connection *connection;
56 :
57 : /**
58 : * Logic for @e ih
59 : */
60 : struct TALER_KYCLOGIC_Plugin *ih_logic;
61 :
62 : /**
63 : * Handle to asynchronously running KYC initiation
64 : * request.
65 : */
66 : struct TALER_KYCLOGIC_InitiateHandle *ih;
67 :
68 : /**
69 : * Subscription for the database event we are
70 : * waiting for.
71 : */
72 : struct GNUNET_DB_EventHandler *eh;
73 :
74 : /**
75 : * Row of the requirement being checked.
76 : */
77 : uint64_t requirement_row;
78 :
79 : /**
80 : * Row of KYC process being initiated.
81 : */
82 : uint64_t process_row;
83 :
84 : /**
85 : * Hash of the payto:// URI we are confirming to
86 : * have finished the KYC for.
87 : */
88 : struct TALER_PaytoHashP h_payto;
89 :
90 : /**
91 : * When will this request time out?
92 : */
93 : struct GNUNET_TIME_Absolute timeout;
94 :
95 : /**
96 : * Set to starting URL of KYC process if KYC is required.
97 : */
98 : char *kyc_url;
99 :
100 : /**
101 : * Set to error details, on error (@ec not TALER_EC_NONE).
102 : */
103 : char *hint;
104 :
105 : /**
106 : * Name of the section of the provider in the configuration.
107 : */
108 : const char *section_name;
109 :
110 : /**
111 : * Set to error encountered with KYC logic, if any.
112 : */
113 : enum TALER_ErrorCode ec;
114 :
115 : /**
116 : * What kind of entity is doing the KYC check?
117 : */
118 : enum TALER_KYCLOGIC_KycUserType ut;
119 :
120 : /**
121 : * True if we are still suspended.
122 : */
123 : bool suspended;
124 :
125 : /**
126 : * False if KYC is not required.
127 : */
128 : bool kyc_required;
129 :
130 : /**
131 : * True if we once tried the KYC initiation.
132 : */
133 : bool ih_done;
134 :
135 : };
136 :
137 :
138 : /**
139 : * Head of list of requests in long polling.
140 : */
141 : static struct KycPoller *kyp_head;
142 :
143 : /**
144 : * Tail of list of requests in long polling.
145 : */
146 : static struct KycPoller *kyp_tail;
147 :
148 :
149 : void
150 0 : TEH_kyc_check_cleanup ()
151 : {
152 : struct KycPoller *kyp;
153 :
154 0 : while (NULL != (kyp = kyp_head))
155 : {
156 0 : GNUNET_CONTAINER_DLL_remove (kyp_head,
157 : kyp_tail,
158 : kyp);
159 0 : if (NULL != kyp->ih)
160 : {
161 0 : kyp->ih_logic->initiate_cancel (kyp->ih);
162 0 : kyp->ih = NULL;
163 : }
164 0 : if (kyp->suspended)
165 : {
166 0 : kyp->suspended = false;
167 0 : MHD_resume_connection (kyp->connection);
168 : }
169 : }
170 0 : }
171 :
172 :
173 : /**
174 : * Function called once a connection is done to
175 : * clean up the `struct ReservePoller` state.
176 : *
177 : * @param rc context to clean up for
178 : */
179 : static void
180 0 : kyp_cleanup (struct TEH_RequestContext *rc)
181 : {
182 0 : struct KycPoller *kyp = rc->rh_ctx;
183 :
184 0 : GNUNET_assert (! kyp->suspended);
185 0 : if (NULL != kyp->eh)
186 : {
187 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
188 : "Cancelling DB event listening\n");
189 0 : TEH_plugin->event_listen_cancel (TEH_plugin->cls,
190 : kyp->eh);
191 0 : kyp->eh = NULL;
192 : }
193 0 : if (NULL != kyp->ih)
194 : {
195 0 : kyp->ih_logic->initiate_cancel (kyp->ih);
196 0 : kyp->ih = NULL;
197 : }
198 0 : GNUNET_free (kyp->kyc_url);
199 0 : GNUNET_free (kyp->hint);
200 0 : GNUNET_free (kyp);
201 0 : }
202 :
203 :
204 : /**
205 : * Function called with the result of a KYC initiation
206 : * operation.
207 : *
208 : * @param cls closure with our `struct KycPoller *`
209 : * @param ec #TALER_EC_NONE on success
210 : * @param redirect_url set to where to redirect the user on success, NULL on failure
211 : * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
212 : * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
213 : * @param error_msg_hint set to additional details to return to user, NULL on success
214 : */
215 : static void
216 0 : initiate_cb (
217 : void *cls,
218 : enum TALER_ErrorCode ec,
219 : const char *redirect_url,
220 : const char *provider_user_id,
221 : const char *provider_legitimization_id,
222 : const char *error_msg_hint)
223 : {
224 0 : struct KycPoller *kyp = cls;
225 : enum GNUNET_DB_QueryStatus qs;
226 :
227 0 : kyp->ih = NULL;
228 0 : kyp->ih_done = true;
229 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230 : "KYC initiation completed with status %d (%s)\n",
231 : ec,
232 : (TALER_EC_NONE == ec)
233 : ? redirect_url
234 : : error_msg_hint);
235 0 : kyp->ec = ec;
236 0 : if (TALER_EC_NONE == ec)
237 : {
238 0 : kyp->kyc_url = GNUNET_strdup (redirect_url);
239 : }
240 : else
241 : {
242 0 : kyp->hint = GNUNET_strdup (error_msg_hint);
243 : }
244 0 : qs = TEH_plugin->update_kyc_process_by_row (
245 0 : TEH_plugin->cls,
246 : kyp->process_row,
247 : kyp->section_name,
248 0 : &kyp->h_payto,
249 : provider_user_id,
250 : provider_legitimization_id,
251 0 : GNUNET_TIME_UNIT_ZERO_ABS);
252 0 : if (qs < 0)
253 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254 : "KYC requirement update failed for %s with status %d at %s:%u\n",
255 : TALER_B2S (&kyp->h_payto),
256 : qs,
257 : __FILE__,
258 : __LINE__);
259 0 : GNUNET_assert (kyp->suspended);
260 0 : kyp->suspended = false;
261 0 : GNUNET_CONTAINER_DLL_remove (kyp_head,
262 : kyp_tail,
263 : kyp);
264 0 : MHD_resume_connection (kyp->connection);
265 0 : TALER_MHD_daemon_trigger ();
266 0 : }
267 :
268 :
269 : /**
270 : * Function implementing database transaction to check wallet's KYC status.
271 : * Runs the transaction logic; IF it returns a non-error code, the transaction
272 : * logic MUST NOT queue a MHD response. IF it returns an hard error, the
273 : * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
274 : * returns the soft error code, the function MAY be called again to retry and
275 : * MUST not queue a MHD response.
276 : *
277 : * @param cls closure with a `struct KycPoller *`
278 : * @param connection MHD request which triggered the transaction
279 : * @param[out] mhd_ret set to MHD response status for @a connection,
280 : * if transaction failed (!)
281 : * @return transaction status
282 : */
283 : static enum GNUNET_DB_QueryStatus
284 0 : kyc_check (void *cls,
285 : struct MHD_Connection *connection,
286 : MHD_RESULT *mhd_ret)
287 : {
288 0 : struct KycPoller *kyp = cls;
289 : enum GNUNET_DB_QueryStatus qs;
290 : struct TALER_KYCLOGIC_ProviderDetails *pd;
291 : enum GNUNET_GenericReturnValue ret;
292 : struct TALER_PaytoHashP h_payto;
293 : char *requirements;
294 :
295 0 : qs = TEH_plugin->lookup_kyc_requirement_by_row (
296 0 : TEH_plugin->cls,
297 : kyp->requirement_row,
298 : &requirements,
299 : &h_payto);
300 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
301 : {
302 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
303 : "No KYC requirements open for %llu\n",
304 : (unsigned long long) kyp->requirement_row);
305 0 : return qs;
306 : }
307 0 : if (qs < 0)
308 : {
309 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
310 0 : return qs;
311 : }
312 0 : if (0 !=
313 0 : GNUNET_memcmp (&kyp->h_payto,
314 : &h_payto))
315 : {
316 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
317 : "Requirement %llu provided, but h_payto does not match\n",
318 : (unsigned long long) kyp->requirement_row);
319 0 : GNUNET_break_op (0);
320 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
321 : MHD_HTTP_FORBIDDEN,
322 : TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
323 : "h_payto");
324 0 : GNUNET_free (requirements);
325 0 : return GNUNET_DB_STATUS_HARD_ERROR;
326 : }
327 0 : if (TALER_KYCLOGIC_check_satisfied (
328 : requirements,
329 : &h_payto,
330 0 : TEH_plugin->select_satisfied_kyc_processes,
331 0 : TEH_plugin->cls))
332 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
333 :
334 0 : kyp->kyc_required = true;
335 0 : ret = TALER_KYCLOGIC_requirements_to_logic (requirements,
336 : kyp->ut,
337 : &kyp->ih_logic,
338 : &pd,
339 : &kyp->section_name);
340 0 : if (GNUNET_OK != ret)
341 : {
342 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
343 : "KYC requirements `%s' cannot be checked, but are set as required in database!\n",
344 : requirements);
345 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
346 : MHD_HTTP_INTERNAL_SERVER_ERROR,
347 : TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE,
348 : requirements);
349 0 : GNUNET_free (requirements);
350 0 : return GNUNET_DB_STATUS_HARD_ERROR;
351 : }
352 0 : GNUNET_free (requirements);
353 :
354 0 : if (kyp->ih_done)
355 0 : return qs;
356 :
357 0 : qs = TEH_plugin->insert_kyc_requirement_process (
358 0 : TEH_plugin->cls,
359 : &h_payto,
360 : kyp->section_name,
361 : NULL,
362 : NULL,
363 : &kyp->process_row);
364 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
365 : "Initiating KYC check with logic %s\n",
366 : kyp->ih_logic->name);
367 0 : kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
368 : pd,
369 : &h_payto,
370 : kyp->process_row,
371 : &initiate_cb,
372 : kyp);
373 0 : GNUNET_break (NULL != kyp->ih);
374 0 : return qs;
375 : }
376 :
377 :
378 : /**
379 : * Function called on events received from Postgres.
380 : * Wakes up long pollers.
381 : *
382 : * @param cls the `struct TEH_RequestContext *`
383 : * @param extra additional event data provided
384 : * @param extra_size number of bytes in @a extra
385 : */
386 : static void
387 0 : db_event_cb (void *cls,
388 : const void *extra,
389 : size_t extra_size)
390 : {
391 0 : struct TEH_RequestContext *rc = cls;
392 0 : struct KycPoller *kyp = rc->rh_ctx;
393 : struct GNUNET_AsyncScopeSave old_scope;
394 :
395 : (void) extra;
396 : (void) extra_size;
397 0 : if (! kyp->suspended)
398 0 : return; /* event triggered while main transaction
399 : was still running, or got multiple wake-up events */
400 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 : "Received KYC update event\n");
402 0 : kyp->suspended = false;
403 0 : GNUNET_async_scope_enter (&rc->async_scope_id,
404 : &old_scope);
405 0 : TEH_check_invariants ();
406 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
407 : "Resuming from long-polling on KYC status\n");
408 0 : GNUNET_CONTAINER_DLL_remove (kyp_head,
409 : kyp_tail,
410 : kyp);
411 0 : MHD_resume_connection (kyp->connection);
412 0 : TALER_MHD_daemon_trigger ();
413 0 : TEH_check_invariants ();
414 0 : GNUNET_async_scope_restore (&old_scope);
415 : }
416 :
417 :
418 : MHD_RESULT
419 0 : TEH_handler_kyc_check (
420 : struct TEH_RequestContext *rc,
421 : const char *const args[3])
422 : {
423 0 : struct KycPoller *kyp = rc->rh_ctx;
424 : MHD_RESULT res;
425 : enum GNUNET_GenericReturnValue ret;
426 : struct GNUNET_TIME_Timestamp now;
427 :
428 0 : if (NULL == kyp)
429 : {
430 0 : kyp = GNUNET_new (struct KycPoller);
431 0 : kyp->connection = rc->connection;
432 0 : rc->rh_ctx = kyp;
433 0 : rc->rh_cleaner = &kyp_cleanup;
434 :
435 : {
436 : unsigned long long requirement_row;
437 : char dummy;
438 :
439 0 : if (1 !=
440 0 : sscanf (args[0],
441 : "%llu%c",
442 : &requirement_row,
443 : &dummy))
444 : {
445 0 : GNUNET_break_op (0);
446 0 : return TALER_MHD_reply_with_error (rc->connection,
447 : MHD_HTTP_BAD_REQUEST,
448 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
449 : "requirement_row");
450 : }
451 0 : kyp->requirement_row = (uint64_t) requirement_row;
452 : }
453 :
454 0 : if (GNUNET_OK !=
455 0 : GNUNET_STRINGS_string_to_data (args[1],
456 0 : strlen (args[1]),
457 0 : &kyp->h_payto,
458 : sizeof (kyp->h_payto)))
459 : {
460 0 : GNUNET_break_op (0);
461 0 : return TALER_MHD_reply_with_error (rc->connection,
462 : MHD_HTTP_BAD_REQUEST,
463 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
464 : "h_payto");
465 : }
466 :
467 0 : if (GNUNET_OK !=
468 0 : TALER_KYCLOGIC_kyc_user_type_from_string (args[2],
469 : &kyp->ut))
470 : {
471 0 : GNUNET_break_op (0);
472 0 : return TALER_MHD_reply_with_error (rc->connection,
473 : MHD_HTTP_BAD_REQUEST,
474 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
475 : "usertype");
476 : }
477 :
478 : {
479 : const char *ts;
480 :
481 0 : ts = MHD_lookup_connection_value (rc->connection,
482 : MHD_GET_ARGUMENT_KIND,
483 : "timeout_ms");
484 0 : if (NULL != ts)
485 : {
486 : char dummy;
487 : unsigned long long tms;
488 :
489 0 : if (1 !=
490 0 : sscanf (ts,
491 : "%llu%c",
492 : &tms,
493 : &dummy))
494 : {
495 0 : GNUNET_break_op (0);
496 0 : return TALER_MHD_reply_with_error (rc->connection,
497 : MHD_HTTP_BAD_REQUEST,
498 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
499 : "timeout_ms");
500 : }
501 0 : kyp->timeout = GNUNET_TIME_relative_to_absolute (
502 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
503 : tms));
504 : }
505 : }
506 : }
507 :
508 0 : if ( (NULL == kyp->eh) &&
509 0 : GNUNET_TIME_absolute_is_future (kyp->timeout) )
510 : {
511 0 : struct TALER_KycCompletedEventP rep = {
512 0 : .header.size = htons (sizeof (rep)),
513 0 : .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
514 : .h_payto = kyp->h_payto
515 : };
516 :
517 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
518 : "Starting DB event listening\n");
519 0 : kyp->eh = TEH_plugin->event_listen (
520 0 : TEH_plugin->cls,
521 : GNUNET_TIME_absolute_get_remaining (kyp->timeout),
522 : &rep.header,
523 : &db_event_cb,
524 : rc);
525 : }
526 :
527 0 : now = GNUNET_TIME_timestamp_get ();
528 0 : ret = TEH_DB_run_transaction (rc->connection,
529 : "kyc check",
530 : TEH_MT_REQUEST_OTHER,
531 : &res,
532 : &kyc_check,
533 : kyp);
534 0 : if (GNUNET_SYSERR == ret)
535 : {
536 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
537 : "Transaction failed.\n");
538 0 : return res;
539 : }
540 :
541 0 : if ( (NULL == kyp->ih) &&
542 0 : (! kyp->kyc_required) )
543 : {
544 : /* KYC not required */
545 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
546 : "KYC not required %llu\n",
547 : (unsigned long long) kyp->requirement_row);
548 0 : return TALER_MHD_reply_static (
549 : rc->connection,
550 : MHD_HTTP_NO_CONTENT,
551 : NULL,
552 : NULL,
553 : 0);
554 : }
555 :
556 0 : if (NULL != kyp->ih)
557 : {
558 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
559 : "Suspending HTTP request on KYC logic...\n");
560 0 : kyp->suspended = true;
561 0 : GNUNET_CONTAINER_DLL_insert (kyp_head,
562 : kyp_tail,
563 : kyp);
564 0 : MHD_suspend_connection (kyp->connection);
565 0 : return MHD_YES;
566 : }
567 :
568 : /* long polling? */
569 0 : if ( (NULL != kyp->section_name) &&
570 0 : GNUNET_TIME_absolute_is_future (kyp->timeout))
571 : {
572 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
573 : "Suspending HTTP request on timeout (%s) now...\n",
574 : GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_duration (
575 : kyp->timeout),
576 : true));
577 0 : GNUNET_assert (NULL != kyp->eh);
578 0 : kyp->suspended = true;
579 0 : GNUNET_CONTAINER_DLL_insert (kyp_head,
580 : kyp_tail,
581 : kyp);
582 0 : MHD_suspend_connection (kyp->connection);
583 0 : return MHD_YES;
584 : }
585 :
586 : /* KYC plugin generated reply? */
587 0 : if (NULL != kyp->kyc_url)
588 : {
589 0 : return TALER_MHD_REPLY_JSON_PACK (
590 : rc->connection,
591 : MHD_HTTP_ACCEPTED,
592 : GNUNET_JSON_pack_string ("kyc_url",
593 : kyp->kyc_url));
594 : }
595 :
596 0 : if (TALER_EC_NONE != kyp->ec)
597 : {
598 0 : return TALER_MHD_reply_with_ec (rc->connection,
599 : kyp->ec,
600 0 : kyp->hint);
601 : }
602 :
603 : /* KYC must have succeeded! */
604 : {
605 : struct TALER_ExchangePublicKeyP pub;
606 : struct TALER_ExchangeSignatureP sig;
607 : enum TALER_ErrorCode ec;
608 :
609 0 : if (TALER_EC_NONE !=
610 0 : (ec = TALER_exchange_online_account_setup_success_sign (
611 : &TEH_keys_exchange_sign_,
612 0 : &kyp->h_payto,
613 : now,
614 : &pub,
615 : &sig)))
616 : {
617 0 : return TALER_MHD_reply_with_ec (rc->connection,
618 : ec,
619 : NULL);
620 : }
621 0 : return TALER_MHD_REPLY_JSON_PACK (
622 : rc->connection,
623 : MHD_HTTP_OK,
624 : GNUNET_JSON_pack_data_auto ("exchange_sig",
625 : &sig),
626 : GNUNET_JSON_pack_data_auto ("exchange_pub",
627 : &pub),
628 : GNUNET_JSON_pack_timestamp ("now",
629 : now));
630 : }
631 : }
632 :
633 :
634 : /* end of taler-exchange-httpd_kyc-check.c */
|