anastasis

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

anastasis-httpd_truth-challenge.c (41785B)


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