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