anastasis

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

anastasis-httpd_truth-challenge.c (41083B)


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