taler-exchange-httpd_aml-decision.c (15081B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023-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. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_aml-decision.c 18 * @brief Handle POST request about an AML decision. 19 * @author Christian Grothoff 20 * @author Florian Dold 21 */ 22 #include "taler/platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_json_lib.h> 25 #include <jansson.h> 26 #include <microhttpd.h> 27 #include <pthread.h> 28 #include "taler/taler_json_lib.h" 29 #include "taler/taler_mhd_lib.h" 30 #include "taler/taler_kyclogic_lib.h" 31 #include "taler/taler_signatures.h" 32 #include "taler-exchange-httpd_common_kyc.h" 33 #include "taler-exchange-httpd_responses.h" 34 #include "taler-exchange-httpd_aml-decision.h" 35 36 37 /** 38 * Context used for processing the AML decision request. 39 */ 40 struct AmlDecisionContext 41 { 42 43 /** 44 * Kept in a DLL. 45 */ 46 struct AmlDecisionContext *next; 47 48 /** 49 * Kept in a DLL. 50 */ 51 struct AmlDecisionContext *prev; 52 53 /** 54 * HTTP status code to use with @e response. 55 */ 56 unsigned int response_code; 57 58 /** 59 * Response to return, NULL if none yet. 60 */ 61 struct MHD_Response *response; 62 63 /** 64 * Request we are processing. 65 */ 66 struct TEH_RequestContext *rc; 67 68 /** 69 * Handle for async KYC processing. 70 */ 71 struct TEH_KycMeasureRunContext *kat; 72 73 }; 74 75 /** 76 * Kept in a DLL. 77 */ 78 static struct AmlDecisionContext *adc_head; 79 80 /** 81 * Kept in a DLL. 82 */ 83 static struct AmlDecisionContext *adc_tail; 84 85 86 void 87 TEH_aml_decision_cleanup () 88 { 89 struct AmlDecisionContext *adc; 90 91 while (NULL != (adc = adc_head)) 92 { 93 MHD_resume_connection (adc->rc->connection); 94 GNUNET_CONTAINER_DLL_remove (adc_head, 95 adc_tail, 96 adc); 97 } 98 } 99 100 101 /** 102 * Function called to clean up aml decision context. 103 * 104 * @param[in,out] rc context to clean up 105 */ 106 static void 107 aml_decision_cleaner (struct TEH_RequestContext *rc) 108 { 109 struct AmlDecisionContext *adc = rc->rh_ctx; 110 111 if (NULL != adc->kat) 112 { 113 TEH_kyc_run_measure_cancel (adc->kat); 114 adc->kat = NULL; 115 } 116 if (NULL != adc->response) 117 { 118 MHD_destroy_response (adc->response); 119 adc->response = NULL; 120 } 121 GNUNET_free (adc); 122 } 123 124 125 /** 126 * Function called after the KYC-AML trigger is done. 127 * 128 * @param cls closure 129 * @param ec error code or 0 on success 130 * @param detail error message or NULL on success / no info 131 */ 132 static void 133 aml_trigger_callback ( 134 void *cls, 135 enum TALER_ErrorCode ec, 136 const char *detail) 137 { 138 struct AmlDecisionContext *adc = cls; 139 140 adc->kat = NULL; 141 GNUNET_assert (NULL == adc->response); 142 143 if (TALER_EC_NONE != ec) 144 { 145 adc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 146 adc->response = TALER_MHD_make_error ( 147 ec, 148 detail); 149 } 150 else 151 { 152 adc->response_code = MHD_HTTP_NO_CONTENT; 153 adc->response = MHD_create_response_from_buffer_static ( 154 0, 155 ""); 156 TALER_MHD_add_global_headers (adc->response, 157 true); 158 } 159 160 GNUNET_assert (NULL != adc->response); 161 162 MHD_resume_connection (adc->rc->connection); 163 GNUNET_CONTAINER_DLL_remove (adc_head, 164 adc_tail, 165 adc); 166 TALER_MHD_daemon_trigger (); 167 } 168 169 170 MHD_RESULT 171 TEH_handler_post_aml_decision ( 172 struct TEH_RequestContext *rc, 173 const struct TALER_AmlOfficerPublicKeyP *officer_pub, 174 const json_t *root) 175 { 176 struct MHD_Connection *connection = rc->connection; 177 struct AmlDecisionContext *adc = rc->rh_ctx; 178 const char *justification; 179 const char *new_measures = NULL; 180 bool to_investigate; 181 struct GNUNET_TIME_Timestamp decision_time; 182 struct GNUNET_TIME_Timestamp attributes_expiration 183 = GNUNET_TIME_UNIT_ZERO_TS; 184 const json_t *new_rules; 185 const json_t *events = NULL; 186 const json_t *properties = NULL; 187 const json_t *attributes = NULL; 188 struct TALER_FullPayto payto_uri = { 189 .full_payto = NULL 190 }; 191 struct TALER_NormalizedPaytoHashP h_payto; 192 struct TALER_AmlOfficerSignatureP officer_sig; 193 struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL; 194 uint64_t legi_measure_serial_id = 0; 195 MHD_RESULT ret; 196 struct GNUNET_JSON_Specification spec[] = { 197 GNUNET_JSON_spec_mark_optional ( 198 GNUNET_JSON_spec_string ( 199 "new_measures", 200 &new_measures), 201 NULL), 202 GNUNET_JSON_spec_string ("justification", 203 &justification), 204 GNUNET_JSON_spec_mark_optional ( 205 TALER_JSON_spec_full_payto_uri ("payto_uri", 206 &payto_uri), 207 NULL), 208 GNUNET_JSON_spec_fixed_auto ("h_payto", 209 &h_payto), 210 GNUNET_JSON_spec_object_const ("new_rules", 211 &new_rules), 212 GNUNET_JSON_spec_mark_optional ( 213 GNUNET_JSON_spec_object_const ("properties", 214 &properties), 215 NULL), 216 GNUNET_JSON_spec_mark_optional ( 217 GNUNET_JSON_spec_array_const ("events", 218 &events), 219 NULL), 220 GNUNET_JSON_spec_bool ("keep_investigating", 221 &to_investigate), 222 GNUNET_JSON_spec_fixed_auto ("officer_sig", 223 &officer_sig), 224 GNUNET_JSON_spec_timestamp ("decision_time", 225 &decision_time), 226 GNUNET_JSON_spec_mark_optional ( 227 GNUNET_JSON_spec_timestamp ("attributes_expiration", 228 &attributes_expiration), 229 NULL), 230 GNUNET_JSON_spec_mark_optional ( 231 GNUNET_JSON_spec_object_const ("attributes", 232 &attributes), 233 NULL), 234 GNUNET_JSON_spec_end () 235 }; 236 struct GNUNET_TIME_Timestamp expiration_time; 237 json_t *jmeasures = NULL; 238 bool is_wallet; 239 size_t eas = 0; 240 void *ea = NULL; 241 242 if (NULL == adc) 243 { 244 /* Initialize context */ 245 adc = GNUNET_new (struct AmlDecisionContext); 246 adc->rc = rc; 247 rc->rh_ctx = adc; 248 rc->rh_cleaner = aml_decision_cleaner; 249 } 250 251 if (NULL != adc->response) 252 { 253 ret = MHD_queue_response (rc->connection, 254 adc->response_code, 255 adc->response); 256 goto done; 257 } 258 259 { 260 enum GNUNET_GenericReturnValue res; 261 262 res = TALER_MHD_parse_json_data (connection, 263 root, 264 spec); 265 if (GNUNET_SYSERR == res) 266 { 267 ret = MHD_NO; /* hard failure */ 268 goto done; 269 } 270 if (GNUNET_NO == res) 271 { 272 GNUNET_break_op (0); 273 ret = MHD_YES /* failure */; 274 goto done; 275 } 276 if ( (NULL != attributes) && 277 (! json_is_string (json_object_get (attributes, 278 "FORM_ID"))) ) 279 { 280 GNUNET_break_op (0); 281 ret = TALER_MHD_reply_with_error ( 282 connection, 283 MHD_HTTP_BAD_REQUEST, 284 TALER_EC_GENERIC_PARAMETER_MALFORMED, 285 "attributes['FORM_ID']"); 286 goto done; 287 } 288 } 289 if (NULL != payto_uri.full_payto) 290 { 291 struct TALER_NormalizedPaytoHashP h_payto2; 292 293 TALER_full_payto_normalize_and_hash (payto_uri, 294 &h_payto2); 295 if (0 != 296 GNUNET_memcmp (&h_payto, 297 &h_payto2)) 298 { 299 GNUNET_break_op (0); 300 ret = TALER_MHD_reply_with_error ( 301 connection, 302 MHD_HTTP_BAD_REQUEST, 303 TALER_EC_GENERIC_PARAMETER_MALFORMED, 304 "payto_uri"); 305 goto done; 306 } 307 } 308 309 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 310 if (GNUNET_OK != 311 TALER_officer_aml_decision_verify ( 312 justification, 313 decision_time, 314 &h_payto, 315 new_rules, 316 properties, 317 new_measures, 318 to_investigate, 319 officer_pub, 320 &officer_sig, 321 attributes_expiration, 322 attributes)) 323 { 324 GNUNET_break_op (0); 325 ret = TALER_MHD_reply_with_error ( 326 connection, 327 MHD_HTTP_FORBIDDEN, 328 TALER_EC_EXCHANGE_AML_DECISION_ADD_SIGNATURE_INVALID, 329 NULL); 330 goto done; 331 } 332 333 lrs = TALER_KYCLOGIC_rules_parse (new_rules); 334 if (NULL == lrs) 335 { 336 GNUNET_break_op (0); 337 ret = TALER_MHD_reply_with_error ( 338 connection, 339 MHD_HTTP_BAD_REQUEST, 340 TALER_EC_GENERIC_PARAMETER_MALFORMED, 341 "legitimization rule malformed"); 342 goto done; 343 } 344 345 expiration_time = TALER_KYCLOGIC_rules_get_expiration (lrs); 346 if ( (NULL != new_measures) && 347 (0 != strlen (new_measures)) ) 348 { 349 jmeasures 350 = TALER_KYCLOGIC_get_jmeasures (lrs, 351 new_measures); 352 if (NULL == jmeasures) 353 { 354 GNUNET_break_op (0); 355 /* Request specified a new_measure for which the given 356 rule set does not work as it does not define the measure */ 357 ret = TALER_MHD_reply_with_error ( 358 connection, 359 MHD_HTTP_BAD_REQUEST, 360 TALER_EC_GENERIC_PARAMETER_MALFORMED, 361 "new_measures/new_rules"); 362 goto done; 363 } 364 } 365 366 { 367 size_t num_events = json_array_size (events); 368 const char *sevents[GNUNET_NZL (num_events)]; 369 enum GNUNET_DB_QueryStatus qs; 370 struct GNUNET_TIME_Timestamp last_date; 371 bool invalid_officer = true; 372 bool unknown_account = false; 373 struct GNUNET_HashCode h_attr = { 0 }; 374 const char *form_id = NULL; 375 376 if (NULL != attributes) 377 { 378 form_id = json_string_value (json_object_get (attributes, 379 "FORM_ID")); 380 TALER_json_hash (attributes, 381 &h_attr); 382 TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, 383 attributes, 384 &ea, 385 &eas); 386 } 387 388 for (size_t i = 0; i<num_events; i++) 389 { 390 sevents[i] = json_string_value (json_array_get (events, 391 i)); 392 if (NULL == sevents[i]) 393 { 394 GNUNET_break_op (0); 395 ret = TALER_MHD_reply_with_error ( 396 connection, 397 MHD_HTTP_BAD_REQUEST, 398 TALER_EC_GENERIC_PARAMETER_MALFORMED, 399 "events"); 400 goto done; 401 } 402 } 403 /* We keep 'new_measures' around mostly so that 404 the auditor can later verify officer_sig */ 405 qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls, 406 payto_uri, 407 &h_payto, 408 decision_time, 409 expiration_time, 410 properties, 411 new_rules, 412 to_investigate, 413 new_measures, 414 jmeasures, 415 justification, 416 officer_pub, 417 &officer_sig, 418 num_events, 419 sevents, 420 form_id, 421 eas, /* enc_attributes_size*/ 422 ea, /* enc_attributes*/ 423 NULL != attributes 424 ? &h_attr 425 : NULL, /* attributes_hash */ 426 attributes_expiration, 427 &invalid_officer, 428 &unknown_account, 429 &last_date, 430 &legi_measure_serial_id, 431 &is_wallet); 432 json_decref (jmeasures); 433 if (qs <= 0) 434 { 435 GNUNET_break (0); 436 ret = TALER_MHD_reply_with_error ( 437 connection, 438 MHD_HTTP_INTERNAL_SERVER_ERROR, 439 TALER_EC_GENERIC_DB_STORE_FAILED, 440 "insert_aml_decision"); 441 goto done; 442 } 443 if (invalid_officer) 444 { 445 GNUNET_break_op (0); 446 ret = TALER_MHD_reply_with_error ( 447 connection, 448 MHD_HTTP_FORBIDDEN, 449 TALER_EC_EXCHANGE_AML_DECISION_INVALID_OFFICER, 450 NULL); 451 goto done; 452 } 453 if (unknown_account) 454 { 455 GNUNET_break_op (0); 456 ret = TALER_MHD_reply_with_error ( 457 connection, 458 MHD_HTTP_NOT_FOUND, 459 TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN, 460 "h_payto"); 461 goto done; 462 } 463 if (GNUNET_TIME_timestamp_cmp (last_date, 464 >, 465 decision_time)) 466 { 467 GNUNET_break_op (0); 468 ret = TALER_MHD_reply_with_error ( 469 connection, 470 MHD_HTTP_CONFLICT, 471 TALER_EC_EXCHANGE_AML_DECISION_MORE_RECENT_PRESENT, 472 NULL); 473 goto done; 474 } 475 } 476 /* Run instant measure if necessary */ 477 { 478 const struct TALER_KYCLOGIC_Measure *instant_ms = NULL; 479 480 if (NULL != new_measures) 481 { 482 instant_ms = TALER_KYCLOGIC_get_instant_measure (lrs, 483 new_measures); 484 } 485 486 if (NULL != instant_ms) 487 { 488 /* We have an 'instant' measure which means we must run the 489 AML program immediately instead of waiting for the account owner 490 to select some measure and contribute their KYC data. */ 491 492 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 493 "Running instant measure after AML decision\n"); 494 adc->kat = TEH_kyc_run_measure_directly ( 495 &rc->async_scope_id, 496 instant_ms, 497 &h_payto, 498 is_wallet, 499 &aml_trigger_callback, 500 adc 501 ); 502 if (NULL == adc->kat) 503 { 504 GNUNET_break (0); 505 ret = TALER_MHD_reply_with_error ( 506 rc->connection, 507 MHD_HTTP_INTERNAL_SERVER_ERROR, 508 TALER_EC_EXCHANGE_KYC_GENERIC_AML_LOGIC_BUG, 509 "TEH_kyc_finished"); 510 goto done; 511 } 512 MHD_suspend_connection (adc->rc->connection); 513 GNUNET_CONTAINER_DLL_insert (adc_head, 514 adc_tail, 515 adc); 516 ret = MHD_YES; 517 goto done; 518 } 519 } 520 ret = TALER_MHD_reply_static ( 521 connection, 522 MHD_HTTP_NO_CONTENT, 523 NULL, 524 NULL, 525 0); 526 goto done; 527 528 done: 529 TALER_KYCLOGIC_rules_free (lrs); 530 GNUNET_free (ea); 531 return ret; 532 } 533 534 535 /* end of taler-exchange-httpd_aml-decision.c */