anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-httpd_truth-solve.c (47395B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2019-2022 Anastasis SARL
      4 
      5   Anastasis 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   Anastasis 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   Anastasis; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis-httpd_truth-solve.c
     18  * @brief functions to handle incoming requests on /truth/$TID/solve
     19  * @author Dennis Neufeld
     20  * @author Dominik Meister
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"
     24 #include "anastasis-httpd.h"
     25 #include "anastasis_service.h"
     26 #include "anastasis-httpd_truth.h"
     27 #include <gnunet/gnunet_util_lib.h>
     28 #include <gnunet/gnunet_rest_lib.h>
     29 #include "anastasis_authorization_lib.h"
     30 #include <taler/taler_merchant_service.h>
     31 #include <taler/taler_json_lib.h>
     32 #include <taler/taler_mhd_lib.h>
     33 #include <taler/taler-merchant/post-private-orders.h>
     34 #include <taler/taler-merchant/get-private-orders-ORDER_ID.h>
     35 
     36 /**
     37  * What is the maximum frequency at which we allow
     38  * clients to attempt to answer security questions?
     39  */
     40 #define MAX_QUESTION_FREQ GNUNET_TIME_relative_multiply ( \
     41           GNUNET_TIME_UNIT_SECONDS, 30)
     42 
     43 /**
     44  * How long should the wallet check for auto-refunds before giving up?
     45  */
     46 #define AUTO_REFUND_TIMEOUT GNUNET_TIME_relative_multiply ( \
     47           GNUNET_TIME_UNIT_MINUTES, 2)
     48 
     49 
     50 /**
     51  * How many retries do we allow per code?
     52  */
     53 #define INITIAL_RETRY_COUNTER 3
     54 
     55 
     56 struct SolveContext
     57 {
     58 
     59   /**
     60    * Payment Identifier
     61    */
     62   struct ANASTASIS_PaymentSecretP payment_identifier;
     63 
     64   /**
     65    * Public key of the challenge which is solved.
     66    */
     67   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     68 
     69   /**
     70    * Key to decrypt the truth.
     71    */
     72   struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
     73 
     74   /**
     75    * Cost for paying the challenge.
     76    */
     77   struct TALER_Amount challenge_cost;
     78 
     79   /**
     80    * Our handler context.
     81    */
     82   struct TM_HandlerContext *hc;
     83 
     84   /**
     85    * Opaque parsing context.
     86    */
     87   void *opaque_post_parsing_context;
     88 
     89   /**
     90    * Uploaded JSON data, NULL if upload is not yet complete.
     91    */
     92   json_t *root;
     93 
     94   /**
     95    * Kept in DLL for shutdown handling while suspended.
     96    */
     97   struct SolveContext *next;
     98 
     99   /**
    100    * Kept in DLL for shutdown handling while suspended.
    101    */
    102   struct SolveContext *prev;
    103 
    104   /**
    105    * Connection handle for closing or resuming
    106    */
    107   struct MHD_Connection *connection;
    108 
    109   /**
    110    * Reference to the authorization plugin which was loaded
    111    */
    112   struct ANASTASIS_AuthorizationPlugin *authorization;
    113 
    114   /**
    115    * Status of the authorization
    116    */
    117   struct ANASTASIS_AUTHORIZATION_State *as;
    118 
    119   /**
    120    * Used while we are awaiting proposal creation.
    121    */
    122   struct TALER_MERCHANT_PostPrivateOrdersHandle *po;
    123 
    124   /**
    125    * Used while we are waiting payment.
    126    */
    127   struct TALER_MERCHANT_GetPrivateOrderHandle *cpo;
    128 
    129   /**
    130    * HTTP response code to use on resume, if non-NULL.
    131    */
    132   struct MHD_Response *resp;
    133 
    134   /**
    135    * Our entry in the #to_heap, or NULL.
    136    */
    137   struct GNUNET_CONTAINER_HeapNode *hn;
    138 
    139   /**
    140    * Challenge response we got from the request.
    141    */
    142   struct GNUNET_HashCode challenge_response;
    143 
    144   /**
    145    * How long do we wait at most for payment or
    146    * authorization?
    147    */
    148   struct GNUNET_TIME_Absolute timeout;
    149 
    150   /**
    151    * Random authorization code we are using.
    152    */
    153   uint64_t code;
    154 
    155   /**
    156    * HTTP response code to use on resume, if resp is set.
    157    */
    158   unsigned int response_code;
    159 
    160   /**
    161    * true if client did not provide a payment secret / order ID.
    162    */
    163   bool no_payment_identifier_provided;
    164 
    165   /**
    166    * True if this entry is in the #gc_head DLL.
    167    */
    168   bool in_list;
    169 
    170   /**
    171    * True if this entry is currently suspended.
    172    */
    173   bool suspended;
    174 
    175 };
    176 
    177 
    178 /**
    179  * Head of linked list over all authorization processes
    180  */
    181 static struct SolveContext *gc_head;
    182 
    183 /**
    184  * Tail of linked list over all authorization processes
    185  */
    186 static struct SolveContext *gc_tail;
    187 
    188 /**
    189  * Task running #do_timeout().
    190  */
    191 static struct GNUNET_SCHEDULER_Task *to_task;
    192 
    193 
    194 /**
    195  * Generate a response telling the client that answering this
    196  * challenge failed because the rate limit has been exceeded.
    197  *
    198  * @param gc request to answer for
    199  * @return MHD status code
    200  */
    201 static MHD_RESULT
    202 reply_rate_limited (const struct SolveContext *gc)
    203 {
    204   if (NULL != gc->authorization)
    205     return TALER_MHD_REPLY_JSON_PACK (
    206       gc->connection,
    207       MHD_HTTP_TOO_MANY_REQUESTS,
    208       TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED),
    209       GNUNET_JSON_pack_uint64 ("request_limit",
    210                                gc->authorization->retry_counter),
    211       GNUNET_JSON_pack_time_rel ("request_frequency",
    212                                  gc->authorization->code_rotation_period));
    213   /* must be security question */
    214   return TALER_MHD_REPLY_JSON_PACK (
    215     gc->connection,
    216     MHD_HTTP_TOO_MANY_REQUESTS,
    217     TALER_MHD_PACK_EC (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED),
    218     GNUNET_JSON_pack_uint64 ("request_limit",
    219                              INITIAL_RETRY_COUNTER),
    220     GNUNET_JSON_pack_time_rel ("request_frequency",
    221                                MAX_QUESTION_FREQ));
    222 }
    223 
    224 
    225 /**
    226  * Timeout requests that are past their due date.
    227  *
    228  * @param cls NULL
    229  */
    230 static void
    231 do_timeout (void *cls)
    232 {
    233   struct SolveContext *gc;
    234 
    235   (void) cls;
    236   to_task = NULL;
    237   while (NULL !=
    238          (gc = GNUNET_CONTAINER_heap_peek (AH_to_heap)))
    239   {
    240     if (GNUNET_TIME_absolute_is_future (gc->timeout))
    241       break;
    242     if (gc->suspended)
    243     {
    244       /* Test needed as we may have a "concurrent"
    245          wakeup from another task that did not clear
    246          this entry from the heap before the
    247          response process concluded. */
    248       gc->suspended = false;
    249       MHD_resume_connection (gc->connection);
    250     }
    251     GNUNET_assert (NULL != gc->hn);
    252     gc->hn = NULL;
    253     GNUNET_assert (gc ==
    254                    GNUNET_CONTAINER_heap_remove_root (AH_to_heap));
    255   }
    256   if (NULL == gc)
    257     return;
    258   to_task = GNUNET_SCHEDULER_add_at (gc->timeout,
    259                                      &do_timeout,
    260                                      NULL);
    261 }
    262 
    263 
    264 void
    265 AH_truth_solve_shutdown (void)
    266 {
    267   struct SolveContext *gc;
    268 
    269   while (NULL != (gc = gc_head))
    270   {
    271     GNUNET_CONTAINER_DLL_remove (gc_head,
    272                                  gc_tail,
    273                                  gc);
    274     gc->in_list = false;
    275     if (NULL != gc->cpo)
    276     {
    277       TALER_MERCHANT_get_private_order_cancel (gc->cpo);
    278       gc->cpo = NULL;
    279     }
    280     if (NULL != gc->po)
    281     {
    282       TALER_MERCHANT_post_private_orders_cancel (gc->po);
    283       gc->po = NULL;
    284     }
    285     if (gc->suspended)
    286     {
    287       gc->suspended = false;
    288       MHD_resume_connection (gc->connection);
    289     }
    290     if (NULL != gc->as)
    291     {
    292       gc->authorization->cleanup (gc->as);
    293       gc->as = NULL;
    294       gc->authorization = NULL;
    295     }
    296   }
    297   ANASTASIS_authorization_plugin_shutdown ();
    298   if (NULL != to_task)
    299   {
    300     GNUNET_SCHEDULER_cancel (to_task);
    301     to_task = NULL;
    302   }
    303 }
    304 
    305 
    306 /**
    307  * Callback used to notify the application about completed requests.
    308  * Cleans up the requests data structures.
    309  *
    310  * @param[in,out] hc
    311  */
    312 static void
    313 request_done (struct TM_HandlerContext *hc)
    314 {
    315   struct SolveContext *gc = hc->ctx;
    316 
    317   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    318               "Request completed\n");
    319   if (NULL == gc)
    320     return;
    321   hc->cc = NULL;
    322   GNUNET_assert (! gc->suspended);
    323   if (gc->in_list)
    324   {
    325     GNUNET_CONTAINER_DLL_remove (gc_head,
    326                                  gc_tail,
    327                                  gc);
    328     gc->in_list = false;
    329   }
    330   if (NULL != gc->hn)
    331   {
    332     GNUNET_assert (gc ==
    333                    GNUNET_CONTAINER_heap_remove_node (gc->hn));
    334     gc->hn = NULL;
    335   }
    336   if (NULL != gc->as)
    337   {
    338     gc->authorization->cleanup (gc->as);
    339     gc->authorization = NULL;
    340     gc->as = NULL;
    341   }
    342   if (NULL != gc->cpo)
    343   {
    344     TALER_MERCHANT_get_private_order_cancel (gc->cpo);
    345     gc->cpo = NULL;
    346   }
    347   if (NULL != gc->po)
    348   {
    349     TALER_MERCHANT_post_private_orders_cancel (gc->po);
    350     gc->po = NULL;
    351   }
    352   if (NULL != gc->root)
    353   {
    354     json_decref (gc->root);
    355     gc->root = NULL;
    356   }
    357   TALER_MHD_parse_post_cleanup_callback (gc->opaque_post_parsing_context);
    358   GNUNET_free (gc);
    359   hc->ctx = NULL;
    360 }
    361 
    362 
    363 /**
    364  * Transmit a payment request for @a order_id on @a connection
    365  *
    366  * @param gc context to make payment request for
    367  */
    368 static void
    369 make_payment_request (struct SolveContext *gc)
    370 {
    371   struct MHD_Response *resp;
    372 
    373   resp = MHD_create_response_from_buffer (0,
    374                                           NULL,
    375                                           MHD_RESPMEM_PERSISTENT);
    376   GNUNET_assert (NULL != resp);
    377   TALER_MHD_add_global_headers (resp,
    378                                 false);
    379   {
    380     char *hdr;
    381     char *order_id;
    382     const char *pfx;
    383     const char *hn;
    384 
    385     if (0 == strncasecmp ("https://",
    386                           AH_backend_url,
    387                           strlen ("https://")))
    388     {
    389       pfx = "taler://";
    390       hn = &AH_backend_url[strlen ("https://")];
    391     }
    392     else if (0 == strncasecmp ("http://",
    393                                AH_backend_url,
    394                                strlen ("http://")))
    395     {
    396       pfx = "taler+http://";
    397       hn = &AH_backend_url[strlen ("http://")];
    398     }
    399     else
    400     {
    401       /* This invariant holds as per check in anastasis-httpd.c */
    402       GNUNET_assert (0);
    403     }
    404     /* This invariant holds as per check in anastasis-httpd.c */
    405     GNUNET_assert (0 != strlen (hn));
    406 
    407     order_id = GNUNET_STRINGS_data_to_string_alloc (
    408       &gc->payment_identifier,
    409       sizeof (gc->payment_identifier));
    410     GNUNET_asprintf (&hdr,
    411                      "%spay/%s%s/",
    412                      pfx,
    413                      hn,
    414                      order_id);
    415     GNUNET_free (order_id);
    416     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    417                 "Sending payment request `%s'\n",
    418                 hdr);
    419     GNUNET_break (MHD_YES ==
    420                   MHD_add_response_header (resp,
    421                                            ANASTASIS_HTTP_HEADER_TALER,
    422                                            hdr));
    423     GNUNET_free (hdr);
    424   }
    425   gc->resp = resp;
    426   gc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
    427 }
    428 
    429 
    430 /**
    431  * Callbacks of this type are used to serve the result of submitting a
    432  * /contract request to a merchant.
    433  *
    434  * @param cls our `struct SolveContext`
    435  * @param por response details
    436  */
    437 static void
    438 proposal_cb (void *cls,
    439              const struct TALER_MERCHANT_PostPrivateOrdersResponse *por)
    440 {
    441   struct SolveContext *gc = cls;
    442   enum GNUNET_DB_QueryStatus qs;
    443 
    444   gc->po = NULL;
    445   GNUNET_assert (gc->in_list);
    446   GNUNET_CONTAINER_DLL_remove (gc_head,
    447                                gc_tail,
    448                                gc);
    449   gc->in_list = false;
    450   GNUNET_assert (gc->suspended);
    451   gc->suspended = false;
    452   MHD_resume_connection (gc->connection);
    453   AH_trigger_daemon (NULL);
    454   if (MHD_HTTP_OK != por->hr.http_status)
    455   {
    456     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    457                 "Backend returned status %u/%d\n",
    458                 por->hr.http_status,
    459                 (int) por->hr.ec);
    460     GNUNET_break (0);
    461     gc->resp = TALER_MHD_MAKE_JSON_PACK (
    462       GNUNET_JSON_pack_uint64 ("code",
    463                                TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR),
    464       GNUNET_JSON_pack_string ("hint",
    465                                "Failed to setup order with merchant backend"),
    466       GNUNET_JSON_pack_uint64 ("backend-ec",
    467                                por->hr.ec),
    468       GNUNET_JSON_pack_uint64 ("backend-http-status",
    469                                por->hr.http_status),
    470       GNUNET_JSON_pack_allow_null (
    471         GNUNET_JSON_pack_object_steal ("backend-reply",
    472                                        (json_t *) por->hr.reply)));
    473     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    474     return;
    475   }
    476   qs = db->record_challenge_payment (db->cls,
    477                                      &gc->truth_uuid,
    478                                      &gc->payment_identifier,
    479                                      &gc->challenge_cost);
    480   if (0 >= qs)
    481   {
    482     GNUNET_break (0);
    483     gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
    484                                      "record challenge payment");
    485     gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    486     return;
    487   }
    488   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    489               "Setup fresh order, creating payment request\n");
    490   make_payment_request (gc);
    491 }
    492 
    493 
    494 /**
    495  * Callback to process a GET /check-payment request
    496  *
    497  * @param cls our `struct SolveContext`
    498  * @param osr order status
    499  */
    500 static void
    501 check_payment_cb (void *cls,
    502                   const struct TALER_MERCHANT_GetPrivateOrderResponse *osr)
    503 
    504 {
    505   struct SolveContext *gc = cls;
    506   const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
    507 
    508   gc->cpo = NULL;
    509   GNUNET_assert (gc->in_list);
    510   GNUNET_CONTAINER_DLL_remove (gc_head,
    511                                gc_tail,
    512                                gc);
    513   gc->in_list = false;
    514   GNUNET_assert (gc->suspended);
    515   gc->suspended = false;
    516   MHD_resume_connection (gc->connection);
    517   AH_trigger_daemon (NULL);
    518 
    519   switch (hr->http_status)
    520   {
    521   case MHD_HTTP_OK:
    522     GNUNET_assert (NULL != osr);
    523     break;
    524   case MHD_HTTP_NOT_FOUND:
    525     /* We created this order before, how can it be not found now? */
    526     GNUNET_break (0);
    527     gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED,
    528                                      NULL);
    529     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    530     return;
    531   case MHD_HTTP_BAD_GATEWAY:
    532     gc->resp = TALER_MHD_make_error (
    533       TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD,
    534       NULL);
    535     gc->response_code = MHD_HTTP_BAD_GATEWAY;
    536     return;
    537   case MHD_HTTP_GATEWAY_TIMEOUT:
    538     gc->resp = TALER_MHD_make_error (TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
    539                                      "Timeout check payment status");
    540     GNUNET_assert (NULL != gc->resp);
    541     gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
    542     return;
    543   default:
    544     {
    545       char status[14];
    546 
    547       GNUNET_snprintf (status,
    548                        sizeof (status),
    549                        "%u",
    550                        hr->http_status);
    551       gc->resp = TALER_MHD_make_error (
    552         TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS,
    553         status);
    554       GNUNET_assert (NULL != gc->resp);
    555       gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    556       return;
    557     }
    558   }
    559 
    560   GNUNET_assert (MHD_HTTP_OK == hr->http_status);
    561   switch (osr->details.ok.status)
    562   {
    563   case TALER_MERCHANT_OSC_PAID:
    564     {
    565       enum GNUNET_DB_QueryStatus qs;
    566 
    567       qs = db->update_challenge_payment (db->cls,
    568                                          &gc->truth_uuid,
    569                                          &gc->payment_identifier);
    570       if (0 <= qs)
    571       {
    572         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    573                     "Order has been paid, continuing with request processing\n")
    574         ;
    575         return; /* continue as planned */
    576       }
    577       GNUNET_break (0);
    578       gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
    579                                        "update challenge payment");
    580       gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
    581       return; /* continue as planned */
    582     }
    583   case TALER_MERCHANT_OSC_CLAIMED:
    584   case TALER_MERCHANT_OSC_UNPAID:
    585     /* repeat payment request */
    586     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    587                 "Order remains unpaid, sending payment request again\n");
    588     make_payment_request (gc);
    589     return;
    590   }
    591   /* should never get here */
    592   GNUNET_break (0);
    593 }
    594 
    595 
    596 /**
    597  * Helper function used to ask our backend to begin processing a
    598  * payment for the user's account.  May perform asynchronous
    599  * operations by suspending the connection if required.
    600  *
    601  * @param gc context to begin payment for.
    602  * @return MHD status code
    603  */
    604 static MHD_RESULT
    605 begin_payment (struct SolveContext *gc)
    606 {
    607   enum GNUNET_DB_QueryStatus qs;
    608   char *order_id;
    609 
    610   qs = db->lookup_challenge_payment (db->cls,
    611                                      &gc->truth_uuid,
    612                                      &gc->payment_identifier);
    613   if (qs < 0)
    614   {
    615     GNUNET_break (0);
    616     return TALER_MHD_reply_with_error (gc->connection,
    617                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    618                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    619                                        "lookup challenge payment");
    620   }
    621   GNUNET_assert (! gc->in_list);
    622   gc->in_list = true;
    623   GNUNET_CONTAINER_DLL_insert (gc_tail,
    624                                gc_head,
    625                                gc);
    626   GNUNET_assert (! gc->suspended);
    627   gc->suspended = true;
    628   MHD_suspend_connection (gc->connection);
    629   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    630   {
    631     /* We already created the order, check if it was paid */
    632     struct GNUNET_TIME_Relative timeout;
    633 
    634     order_id = GNUNET_STRINGS_data_to_string_alloc (
    635       &gc->payment_identifier,
    636       sizeof (gc->payment_identifier));
    637     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    638                 "Order exists, checking payment status for order `%s'\n",
    639                 order_id);
    640     timeout = GNUNET_TIME_absolute_get_remaining (gc->timeout);
    641     gc->cpo = TALER_MERCHANT_get_private_order_create (AH_ctx,
    642                                                        AH_backend_url,
    643                                                        order_id);
    644     GNUNET_assert (NULL != gc->cpo);
    645     GNUNET_assert (
    646       GNUNET_OK ==
    647       TALER_MERCHANT_get_private_order_set_options (
    648         gc->cpo,
    649         TALER_MERCHANT_get_private_order_option_timeout (timeout)));
    650     GNUNET_assert (TALER_EC_NONE ==
    651                    TALER_MERCHANT_get_private_order_start (gc->cpo,
    652                                                            &check_payment_cb,
    653                                                            gc));
    654   }
    655   else
    656   {
    657     /* Create a fresh order */
    658     struct GNUNET_TIME_Timestamp pay_deadline;
    659 
    660     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    661                                 &gc->payment_identifier,
    662                                 sizeof (struct ANASTASIS_PaymentSecretP));
    663     order_id = GNUNET_STRINGS_data_to_string_alloc (
    664       &gc->payment_identifier,
    665       sizeof (gc->payment_identifier));
    666     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    667                 "Creating fresh order `%s'\n",
    668                 order_id);
    669     pay_deadline = GNUNET_TIME_relative_to_timestamp (
    670       ANASTASIS_CHALLENGE_OFFER_LIFETIME);
    671     {
    672       json_t *order;
    673 
    674       order = GNUNET_JSON_PACK (
    675         TALER_JSON_pack_amount ("amount",
    676                                 &gc->challenge_cost),
    677         GNUNET_JSON_pack_string ("summary",
    678                                  "challenge fee for anastasis service"),
    679         GNUNET_JSON_pack_string ("order_id",
    680                                  order_id),
    681         GNUNET_JSON_pack_time_rel ("auto_refund",
    682                                    AUTO_REFUND_TIMEOUT),
    683         GNUNET_JSON_pack_timestamp ("pay_deadline",
    684                                     pay_deadline));
    685       gc->po = TALER_MERCHANT_post_private_orders_create (AH_ctx,
    686                                                           AH_backend_url,
    687                                                           order);
    688       json_decref (order);
    689     }
    690     GNUNET_assert (
    691       GNUNET_OK ==
    692       TALER_MERCHANT_post_private_orders_set_options (
    693         gc->po,
    694         TALER_MERCHANT_post_private_orders_option_create_token (false),
    695         TALER_MERCHANT_post_private_orders_option_refund_delay (
    696           AUTO_REFUND_TIMEOUT)));
    697     GNUNET_assert (TALER_EC_NONE ==
    698                    TALER_MERCHANT_post_private_orders_start (gc->po,
    699                                                              &proposal_cb,
    700                                                              gc));
    701   }
    702   GNUNET_free (order_id);
    703   AH_trigger_curl ();
    704   return MHD_YES;
    705 }
    706 
    707 
    708 /**
    709  * Load encrypted keyshare from db and return it to the client.
    710  *
    711  * @param truth_uuid UUID to the truth for the looup
    712  * @param connection the connection to respond upon
    713  * @return MHD status code
    714  */
    715 static MHD_RESULT
    716 return_key_share (
    717   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    718   struct MHD_Connection *connection)
    719 {
    720   struct ANASTASIS_CRYPTO_EncryptedKeyShareP encrypted_keyshare;
    721 
    722   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    723               "Returning key share of %s\n",
    724               TALER_B2S (truth_uuid));
    725   {
    726     enum GNUNET_DB_QueryStatus qs;
    727 
    728     qs = db->get_key_share (db->cls,
    729                             truth_uuid,
    730                             &encrypted_keyshare);
    731     switch (qs)
    732     {
    733     case GNUNET_DB_STATUS_HARD_ERROR:
    734     case GNUNET_DB_STATUS_SOFT_ERROR:
    735       GNUNET_break (0);
    736       return TALER_MHD_reply_with_error (connection,
    737                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    738                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    739                                          "get key share");
    740     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    741       /* this should be "impossible", after all the
    742          client was able to solve the challenge!
    743          (Exception: we deleted the truth via GC
    744          just while the client was trying to recover.
    745          Alas, highly unlikely...) */
    746       GNUNET_break (0);
    747       return TALER_MHD_reply_with_error (connection,
    748                                          MHD_HTTP_NOT_FOUND,
    749                                          TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE,
    750                                          NULL);
    751     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    752       break;
    753     }
    754   }
    755 
    756   {
    757     struct MHD_Response *resp;
    758     MHD_RESULT ret;
    759 
    760     resp = MHD_create_response_from_buffer (sizeof (encrypted_keyshare),
    761                                             &encrypted_keyshare,
    762                                             MHD_RESPMEM_MUST_COPY);
    763     TALER_MHD_add_global_headers (resp,
    764                                   false);
    765     ret = MHD_queue_response (connection,
    766                               MHD_HTTP_OK,
    767                               resp);
    768     MHD_destroy_response (resp);
    769     return ret;
    770   }
    771 }
    772 
    773 
    774 /**
    775  * Mark @a gc as suspended and update the respective
    776  * data structures and jobs.
    777  *
    778  * @param[in,out] gc context of the suspended operation
    779  */
    780 static void
    781 gc_suspended (struct SolveContext *gc)
    782 {
    783   GNUNET_assert (NULL == gc->hn);
    784   GNUNET_assert (! gc->suspended);
    785   gc->suspended = true;
    786   if (NULL == AH_to_heap)
    787     AH_to_heap = GNUNET_CONTAINER_heap_create (
    788       GNUNET_CONTAINER_HEAP_ORDER_MIN);
    789   gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
    790                                          gc,
    791                                          gc->timeout.abs_value_us);
    792   if (NULL != to_task)
    793   {
    794     GNUNET_SCHEDULER_cancel (to_task);
    795     to_task = NULL;
    796   }
    797   {
    798     struct SolveContext *rn;
    799 
    800     rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
    801     to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
    802                                        &do_timeout,
    803                                        NULL);
    804   }
    805 }
    806 
    807 
    808 /**
    809  * Run the authorization method-specific 'process' function and continue
    810  * based on its result with generating an HTTP response.
    811  *
    812  * @param connection the connection we are handling
    813  * @param gc our overall handler context
    814  */
    815 static MHD_RESULT
    816 run_authorization_process (struct MHD_Connection *connection,
    817                            struct SolveContext *gc)
    818 {
    819   enum ANASTASIS_AUTHORIZATION_SolveResult ret;
    820 
    821   GNUNET_assert (! gc->suspended);
    822   if (NULL == gc->authorization->solve)
    823   {
    824     GNUNET_break (0);
    825     return TALER_MHD_reply_with_error (gc->connection,
    826                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    827                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
    828                                        "solve method not implemented for authorization method");
    829   }
    830   ret = gc->authorization->solve (gc->as,
    831                                   gc->timeout,
    832                                   &gc->challenge_response,
    833                                   connection);
    834   switch (ret)
    835   {
    836   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
    837     /* connection was suspended */
    838     gc_suspended (gc);
    839     return MHD_YES;
    840   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
    841     gc->authorization->cleanup (gc->as);
    842     gc->as = NULL;
    843     return MHD_YES;
    844   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
    845     gc->authorization->cleanup (gc->as);
    846     gc->as = NULL;
    847     return MHD_NO;
    848   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
    849     GNUNET_assert (! gc->suspended);
    850     gc->authorization->cleanup (gc->as);
    851     gc->as = NULL;
    852     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    853                 "Resuming with authorization successful!\n");
    854     if (gc->in_list)
    855     {
    856       GNUNET_CONTAINER_DLL_remove (gc_head,
    857                                    gc_tail,
    858                                    gc);
    859       gc->in_list = false;
    860     }
    861     return MHD_YES;
    862   }
    863   GNUNET_break (0);
    864   return MHD_NO;
    865 }
    866 
    867 
    868 /**
    869  * Use the database to rate-limit queries to the authentication
    870  * procedure, but without actually storing 'real' challenge codes.
    871  *
    872  * @param[in,out] gc context to rate limit requests for
    873  * @return #GNUNET_OK if rate-limiting passes,
    874  *         #GNUNET_NO if a reply was sent (rate limited)
    875  *         #GNUNET_SYSERR if we failed and no reply
    876  *                        was queued
    877  */
    878 static enum GNUNET_GenericReturnValue
    879 rate_limit (struct SolveContext *gc)
    880 {
    881   enum GNUNET_DB_QueryStatus qs;
    882   struct GNUNET_TIME_Timestamp rt;
    883   uint64_t code;
    884   enum ANASTASIS_DB_CodeStatus cs;
    885   struct GNUNET_HashCode hc;
    886   bool satisfied;
    887   uint64_t dummy;
    888 
    889   rt = GNUNET_TIME_UNIT_FOREVER_TS;
    890   qs = db->create_challenge_code (db->cls,
    891                                   &gc->truth_uuid,
    892                                   MAX_QUESTION_FREQ,
    893                                   GNUNET_TIME_UNIT_HOURS,
    894                                   INITIAL_RETRY_COUNTER,
    895                                   &rt,
    896                                   &code);
    897   if (0 > qs)
    898   {
    899     GNUNET_break (0 < qs);
    900     return (MHD_YES ==
    901             TALER_MHD_reply_with_error (gc->connection,
    902                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    903                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    904                                         "create_challenge_code (for rate limiting)"))
    905            ? GNUNET_NO
    906            : GNUNET_SYSERR;
    907   }
    908   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    909   {
    910     return (MHD_YES ==
    911             reply_rate_limited (gc))
    912            ? GNUNET_NO
    913            : GNUNET_SYSERR;
    914   }
    915   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    916               "Using intentionally wrong answer to produce rate-limiting\n");
    917   /* decrement trial counter */
    918   ANASTASIS_hash_answer (code + 1,      /* always use wrong answer */
    919                          &hc);
    920   cs = db->verify_challenge_code (db->cls,
    921                                   &gc->truth_uuid,
    922                                   &hc,
    923                                   &dummy,
    924                                   &satisfied);
    925   switch (cs)
    926   {
    927   case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
    928     /* good, what we wanted */
    929     return GNUNET_OK;
    930   case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
    931   case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
    932     GNUNET_break (0);
    933     return (MHD_YES ==
    934             TALER_MHD_reply_with_error (gc->connection,
    935                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    936                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    937                                         "verify_challenge_code"))
    938            ? GNUNET_NO
    939            : GNUNET_SYSERR;
    940   case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
    941     return (MHD_YES ==
    942             reply_rate_limited (gc))
    943            ? GNUNET_NO
    944            : GNUNET_SYSERR;
    945   case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
    946     /* this should be impossible, we used code+1 */
    947     GNUNET_assert (0);
    948   }
    949   return GNUNET_SYSERR;
    950 }
    951 
    952 
    953 /**
    954  * Handle special case of a security question where we do not
    955  * generate a code. Rate limits answers against brute forcing.
    956  *
    957  * @param[in,out] gc request to handle
    958  * @param decrypted_truth hash to check against
    959  * @param decrypted_truth_size number of bytes in @a decrypted_truth
    960  * @return MHD status code
    961  */
    962 static MHD_RESULT
    963 handle_security_question (struct SolveContext *gc,
    964                           const void *decrypted_truth,
    965                           size_t decrypted_truth_size)
    966 {
    967   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    968               "Handling security question challenge\n");
    969   /* rate limit */
    970   {
    971     enum GNUNET_GenericReturnValue ret;
    972 
    973     ret = rate_limit (gc);
    974     if (GNUNET_OK != ret)
    975       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    976   }
    977   /* check reply matches truth */
    978   if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
    979        (0 != memcmp (&gc->challenge_response,
    980                      decrypted_truth,
    981                      decrypted_truth_size)) )
    982   {
    983     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    984                 "Wrong answer provided to secure question had %u bytes, wanted %u\n",
    985                 (unsigned int) decrypted_truth_size,
    986                 (unsigned int) sizeof (struct GNUNET_HashCode));
    987     return TALER_MHD_reply_with_error (gc->connection,
    988                                        MHD_HTTP_FORBIDDEN,
    989                                        TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
    990                                        NULL);
    991   }
    992   /* good, return the key share */
    993   return return_key_share (&gc->truth_uuid,
    994                            gc->connection);
    995 }
    996 
    997 
    998 /**
    999  * Handle special case of an answer being directly checked by the
   1000  * plugin and not by our database.  Also ensures that the
   1001  * request is rate-limited.
   1002  *
   1003  * @param[in,out] gc request to handle
   1004  * @param decrypted_truth hash to check against
   1005  * @param decrypted_truth_size number of bytes in @a decrypted_truth
   1006  * @return MHD status code
   1007  */
   1008 static MHD_RESULT
   1009 direct_validation (struct SolveContext *gc,
   1010                    const void *decrypted_truth,
   1011                    size_t decrypted_truth_size)
   1012 {
   1013   /* Non-random code, call plugin directly! */
   1014   enum ANASTASIS_AUTHORIZATION_SolveResult aar;
   1015   enum GNUNET_GenericReturnValue ret;
   1016 
   1017   ret = rate_limit (gc);
   1018   if (GNUNET_OK != ret)
   1019     return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
   1020   gc->as = gc->authorization->start (gc->authorization->cls,
   1021                                      &AH_trigger_daemon,
   1022                                      NULL,
   1023                                      &gc->truth_uuid,
   1024                                      0LLU,
   1025                                      decrypted_truth,
   1026                                      decrypted_truth_size);
   1027   if (NULL == gc->as)
   1028   {
   1029     GNUNET_break (0);
   1030     return TALER_MHD_reply_with_error (gc->connection,
   1031                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1032                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1033                                        NULL);
   1034   }
   1035   if (NULL == gc->authorization->solve)
   1036   {
   1037     GNUNET_break (0);
   1038     return TALER_MHD_reply_with_error (gc->connection,
   1039                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1040                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1041                                        "solve method not implemented for authorization method");
   1042   }
   1043   aar = gc->authorization->solve (gc->as,
   1044                                   gc->timeout,
   1045                                   &gc->challenge_response,
   1046                                   gc->connection);
   1047   switch (aar)
   1048   {
   1049   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
   1050     return MHD_YES;
   1051   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
   1052     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1053                 "Suspending request handling\n");
   1054     gc_suspended (gc);
   1055     return MHD_YES;
   1056   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
   1057     return MHD_NO;
   1058   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
   1059     return return_key_share (&gc->truth_uuid,
   1060                              gc->connection);
   1061   }
   1062   GNUNET_break (0);
   1063   return MHD_NO;
   1064 }
   1065 
   1066 
   1067 /**
   1068  * Handle special case of an answer being checked
   1069  * by the plugin asynchronously (IBAN) after we inverted
   1070  * the hash using the database.
   1071  *
   1072  * @param[in,out] gc request to handle
   1073  * @param code validation code provided by the client
   1074  * @param decrypted_truth hash to check against
   1075  * @param decrypted_truth_size number of bytes in @a decrypted_truth
   1076  * @return MHD status code
   1077  */
   1078 static MHD_RESULT
   1079 iban_validation (struct SolveContext *gc,
   1080                  uint64_t code,
   1081                  const void *decrypted_truth,
   1082                  size_t decrypted_truth_size)
   1083 {
   1084   enum ANASTASIS_AUTHORIZATION_SolveResult aar;
   1085 
   1086   gc->as = gc->authorization->start (gc->authorization->cls,
   1087                                      &AH_trigger_daemon,
   1088                                      NULL,
   1089                                      &gc->truth_uuid,
   1090                                      code,
   1091                                      decrypted_truth,
   1092                                      decrypted_truth_size);
   1093   if (NULL == gc->as)
   1094   {
   1095     GNUNET_break (0);
   1096     return TALER_MHD_reply_with_error (gc->connection,
   1097                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1098                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1099                                        NULL);
   1100   }
   1101   if (NULL == gc->authorization->solve)
   1102   {
   1103     GNUNET_break (0);
   1104     return TALER_MHD_reply_with_error (gc->connection,
   1105                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
   1106                                        TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
   1107                                        "solve method not implemented for authorization method");
   1108   }
   1109   aar = gc->authorization->solve (gc->as,
   1110                                   gc->timeout,
   1111                                   &gc->challenge_response,
   1112                                   gc->connection);
   1113   switch (aar)
   1114   {
   1115   case ANASTASIS_AUTHORIZATION_SRES_FAILED:
   1116     return MHD_YES;
   1117   case ANASTASIS_AUTHORIZATION_SRES_SUSPENDED:
   1118     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1119                 "Suspending request handling\n");
   1120     gc_suspended (gc);
   1121     return MHD_YES;
   1122   case ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED:
   1123     return MHD_NO;
   1124   case ANASTASIS_AUTHORIZATION_SRES_FINISHED:
   1125     return return_key_share (&gc->truth_uuid,
   1126                              gc->connection);
   1127   }
   1128   GNUNET_break (0);
   1129   return MHD_NO;
   1130 }
   1131 
   1132 
   1133 MHD_RESULT
   1134 AH_handler_truth_solve (
   1135   struct MHD_Connection *connection,
   1136   struct TM_HandlerContext *hc,
   1137   const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
   1138   const char *upload_data,
   1139   size_t *upload_data_size)
   1140 {
   1141   struct SolveContext *gc = hc->ctx;
   1142   void *encrypted_truth;
   1143   size_t encrypted_truth_size;
   1144   void *decrypted_truth;
   1145   size_t decrypted_truth_size;
   1146   char *truth_mime = NULL;
   1147   bool is_question;
   1148 
   1149   if (NULL == gc)
   1150   {
   1151     /* Fresh request, do initial setup */
   1152     gc = GNUNET_new (struct SolveContext);
   1153     gc->hc = hc;
   1154     hc->ctx = gc;
   1155     gc->connection = connection;
   1156     gc->truth_uuid = *truth_uuid;
   1157     gc->hc->cc = &request_done;
   1158     gc->timeout = GNUNET_TIME_relative_to_absolute (
   1159       GNUNET_TIME_UNIT_SECONDS);
   1160     TALER_MHD_parse_request_timeout (connection,
   1161                                      &gc->timeout);
   1162   } /* end of first-time initialization (if NULL == gc) */
   1163   else
   1164   {
   1165     /* might have been woken up by authorization plugin,
   1166        so clear the flag. MDH called us, so we are
   1167        clearly no longer suspended */
   1168     gc->suspended = false;
   1169     if (NULL != gc->resp)
   1170     {
   1171       MHD_RESULT ret;
   1172 
   1173       /* We generated a response asynchronously, queue that */
   1174       ret = MHD_queue_response (connection,
   1175                                 gc->response_code,
   1176                                 gc->resp);
   1177       GNUNET_break (MHD_YES == ret);
   1178       MHD_destroy_response (gc->resp);
   1179       gc->resp = NULL;
   1180       return ret;
   1181     }
   1182     if (NULL != gc->as)
   1183     {
   1184       /* Authorization process is "running", check what is going on */
   1185       GNUNET_assert (NULL != gc->authorization);
   1186       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1187                   "Continuing with running the authorization process\n");
   1188       GNUNET_assert (! gc->suspended);
   1189       return run_authorization_process (connection,
   1190                                         gc);
   1191     }
   1192     /* We get here if the async check for payment said this request
   1193        was indeed paid! */
   1194   }
   1195 
   1196   if (NULL == gc->root)
   1197   {
   1198     /* parse byte stream upload into JSON */
   1199     enum GNUNET_GenericReturnValue res;
   1200 
   1201     res = TALER_MHD_parse_post_json (connection,
   1202                                      &gc->opaque_post_parsing_context,
   1203                                      upload_data,
   1204                                      upload_data_size,
   1205                                      &gc->root);
   1206     if (GNUNET_SYSERR == res)
   1207     {
   1208       GNUNET_assert (NULL == gc->root);
   1209       return MHD_NO; /* bad upload, could not even generate error */
   1210     }
   1211     if ( (GNUNET_NO == res) ||
   1212          (NULL == gc->root) )
   1213     {
   1214       GNUNET_assert (NULL == gc->root);
   1215       return MHD_YES; /* so far incomplete upload or parser error */
   1216     }
   1217 
   1218     /* 'root' is now initialized, parse JSON body */
   1219     {
   1220       struct GNUNET_JSON_Specification spec[] = {
   1221         GNUNET_JSON_spec_fixed_auto ("truth_decryption_key",
   1222                                      &gc->truth_key),
   1223         GNUNET_JSON_spec_fixed_auto ("h_response",
   1224                                      &gc->challenge_response),
   1225         GNUNET_JSON_spec_mark_optional (
   1226           GNUNET_JSON_spec_fixed_auto ("payment_secret",
   1227                                        &gc->payment_identifier),
   1228           &gc->no_payment_identifier_provided),
   1229         GNUNET_JSON_spec_end ()
   1230       };
   1231       enum GNUNET_GenericReturnValue pres;
   1232 
   1233       pres = TALER_MHD_parse_json_data (connection,
   1234                                         gc->root,
   1235                                         spec);
   1236       if (GNUNET_SYSERR == pres)
   1237       {
   1238         GNUNET_break (0);
   1239         return MHD_NO; /* hard failure */
   1240       }
   1241       if (GNUNET_NO == pres)
   1242       {
   1243         GNUNET_break_op (0);
   1244         return MHD_YES; /* failure */
   1245       }
   1246       if (! gc->no_payment_identifier_provided)
   1247         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1248                     "Client provided payment identifier `%s'\n",
   1249                     TALER_B2S (&gc->payment_identifier));
   1250     }
   1251   }
   1252 
   1253   {
   1254     /* load encrypted truth from DB; we may do this repeatedly
   1255        while handling the same request, if payment was checked
   1256        asynchronously! */
   1257     enum GNUNET_DB_QueryStatus qs;
   1258     char *method;
   1259 
   1260     qs = db->get_escrow_challenge (db->cls,
   1261                                    &gc->truth_uuid,
   1262                                    &encrypted_truth,
   1263                                    &encrypted_truth_size,
   1264                                    &truth_mime,
   1265                                    &method);
   1266     switch (qs)
   1267     {
   1268     case GNUNET_DB_STATUS_HARD_ERROR:
   1269     case GNUNET_DB_STATUS_SOFT_ERROR:
   1270       GNUNET_break (0);
   1271       return TALER_MHD_reply_with_error (gc->connection,
   1272                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1273                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1274                                          "get escrow challenge");
   1275     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1276       return TALER_MHD_reply_with_error (connection,
   1277                                          MHD_HTTP_NOT_FOUND,
   1278                                          TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
   1279                                          NULL);
   1280     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1281       break;
   1282     }
   1283     is_question = (0 == strcmp ("question",
   1284                                 method));
   1285     if (! is_question)
   1286     {
   1287       gc->authorization
   1288         = ANASTASIS_authorization_plugin_load (method,
   1289                                                db,
   1290                                                AH_cfg);
   1291       if (NULL == gc->authorization)
   1292       {
   1293         MHD_RESULT ret;
   1294 
   1295         ret = TALER_MHD_reply_with_error (
   1296           connection,
   1297           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1298           TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED,
   1299           method);
   1300         GNUNET_free (encrypted_truth);
   1301         GNUNET_free (truth_mime);
   1302         GNUNET_free (method);
   1303         return ret;
   1304       }
   1305       gc->challenge_cost = gc->authorization->cost;
   1306     }
   1307     else
   1308     {
   1309       gc->challenge_cost = AH_question_cost;
   1310     }
   1311     GNUNET_free (method);
   1312   }
   1313 
   1314   /* check for payment */
   1315   if ( (is_question) ||
   1316        (! gc->authorization->payment_plugin_managed) )
   1317   {
   1318     if (! TALER_amount_is_zero (&gc->challenge_cost))
   1319     {
   1320       /* Check database to see if the transaction is paid for */
   1321       enum GNUNET_DB_QueryStatus qs;
   1322       bool paid;
   1323 
   1324       if (gc->no_payment_identifier_provided)
   1325       {
   1326         GNUNET_free (truth_mime);
   1327         GNUNET_free (encrypted_truth);
   1328         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1329                     "Beginning payment, client did not provide payment identifier\n");
   1330         return begin_payment (gc);
   1331       }
   1332       qs = db->check_challenge_payment (db->cls,
   1333                                         &gc->payment_identifier,
   1334                                         &gc->truth_uuid,
   1335                                         &paid);
   1336       switch (qs)
   1337       {
   1338       case GNUNET_DB_STATUS_HARD_ERROR:
   1339       case GNUNET_DB_STATUS_SOFT_ERROR:
   1340         GNUNET_break (0);
   1341         GNUNET_free (truth_mime);
   1342         GNUNET_free (encrypted_truth);
   1343         return TALER_MHD_reply_with_error (gc->connection,
   1344                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
   1345                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
   1346                                            "check challenge payment");
   1347       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1348         /* Create fresh payment identifier (cannot trust client) */
   1349         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1350                     "Client-provided payment identifier is unknown.\n");
   1351         GNUNET_free (truth_mime);
   1352         GNUNET_free (encrypted_truth);
   1353         return begin_payment (gc);
   1354       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1355         if (! paid)
   1356         {
   1357           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1358                       "Payment identifier known. Checking payment with client's payment identifier\n");
   1359           GNUNET_free (truth_mime);
   1360           GNUNET_free (encrypted_truth);
   1361           return begin_payment (gc);
   1362         }
   1363         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1364                     "Payment confirmed\n");
   1365         break;
   1366       }
   1367     }
   1368     else
   1369     {
   1370       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1371                   "Request is free of charge\n");
   1372     }
   1373   }
   1374 
   1375   /* We've been paid, now validate the response */
   1376   /* decrypt encrypted_truth */
   1377   ANASTASIS_CRYPTO_truth_decrypt (&gc->truth_key,
   1378                                   encrypted_truth,
   1379                                   encrypted_truth_size,
   1380                                   &decrypted_truth,
   1381                                   &decrypted_truth_size);
   1382   GNUNET_free (encrypted_truth);
   1383   if (NULL == decrypted_truth)
   1384   {
   1385     /* most likely, the decryption key is simply wrong */
   1386     GNUNET_break_op (0);
   1387     GNUNET_free (truth_mime);
   1388     return TALER_MHD_reply_with_error (connection,
   1389                                        MHD_HTTP_BAD_REQUEST,
   1390                                        TALER_EC_ANASTASIS_TRUTH_DECRYPTION_FAILED,
   1391                                        NULL);
   1392   }
   1393 
   1394   /* Special case for secure question: we do not generate a numeric challenge,
   1395      but check that the hash matches */
   1396   if (is_question)
   1397   {
   1398     MHD_RESULT ret;
   1399 
   1400     ret = handle_security_question (gc,
   1401                                     decrypted_truth,
   1402                                     decrypted_truth_size);
   1403     GNUNET_free (truth_mime);
   1404     GNUNET_free (decrypted_truth);
   1405     return ret;
   1406   }
   1407 
   1408   /* Not security question, check for answer in DB */
   1409   {
   1410     enum ANASTASIS_DB_CodeStatus cs;
   1411     bool satisfied = false;
   1412     uint64_t code;
   1413 
   1414     GNUNET_free (truth_mime);
   1415     if (gc->authorization->user_provided_code)
   1416     {
   1417       MHD_RESULT res;
   1418 
   1419       if (GNUNET_TIME_absolute_is_past (gc->timeout))
   1420       {
   1421         GNUNET_free (decrypted_truth);
   1422         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1423                     "Timeout with user provided code\n");
   1424         return TALER_MHD_reply_with_error (connection,
   1425                                            MHD_HTTP_FORBIDDEN,
   1426                                            TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER,
   1427                                            "timeout awaiting validation");
   1428       }
   1429       res = direct_validation (gc,
   1430                                decrypted_truth,
   1431                                decrypted_truth_size);
   1432       GNUNET_free (decrypted_truth);
   1433       return res;
   1434     }
   1435 
   1436     /* random code, check against database */
   1437     cs = db->verify_challenge_code (db->cls,
   1438                                     &gc->truth_uuid,
   1439                                     &gc->challenge_response,
   1440                                     &code,
   1441                                     &satisfied);
   1442     switch (cs)
   1443     {
   1444     case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
   1445       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1446                   "Provided response does not match our stored challenge\n");
   1447       GNUNET_free (decrypted_truth);
   1448       return TALER_MHD_reply_with_error (connection,
   1449                                          MHD_HTTP_FORBIDDEN,
   1450                                          TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
   1451                                          NULL);
   1452     case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
   1453     case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
   1454       GNUNET_break (0);
   1455       GNUNET_free (decrypted_truth);
   1456       return TALER_MHD_reply_with_error (gc->connection,
   1457                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
   1458                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
   1459                                          "verify_challenge_code");
   1460     case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
   1461       GNUNET_free (decrypted_truth);
   1462       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1463                   "Specified challenge code %s was not issued\n",
   1464                   GNUNET_h2s (&gc->challenge_response));
   1465       return TALER_MHD_reply_with_error (connection,
   1466                                          MHD_HTTP_FORBIDDEN,
   1467                                          TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN,
   1468                                          "specific challenge code was not issued");
   1469     case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
   1470       if (! satisfied)
   1471       {
   1472         MHD_RESULT res;
   1473 
   1474         res = iban_validation (gc,
   1475                                code,
   1476                                decrypted_truth,
   1477                                decrypted_truth_size);
   1478         GNUNET_free (decrypted_truth);
   1479         return res;
   1480       }
   1481       GNUNET_free (decrypted_truth);
   1482       return return_key_share (&gc->truth_uuid,
   1483                                connection);
   1484     default:
   1485       GNUNET_break (0);
   1486       return MHD_NO;
   1487     }
   1488   }
   1489 }