anastasis

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

anastasis-httpd_truth-solve.c (47007B)


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