anastasis

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

anastasis-httpd_truth-challenge.c (39960B)


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