anastasis

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

anastasis_authorization_plugin_iban.c (23569B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2021 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.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file anastasis_authorization_plugin_iban.c
     18  * @brief authorization plugin wire transfer based
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "anastasis_authorization_plugin.h"
     23 #include <taler/taler_mhd_lib.h>
     24 #include <taler/taler_json_lib.h>
     25 #include <gnunet/gnunet_db_lib.h>
     26 #include "anastasis_authorization_lib.h"
     27 #include "anastasis_database_lib.h"
     28 #include "anastasis_util_lib.h"
     29 #include "iban.h"
     30 
     31 /**
     32  * How long is a code valid once generated? Very long
     33  * here as we do not want to refuse authentication
     34  * just because the user took a while to execute the
     35  * wire transfer (and then get back to their recovery
     36  * operation).
     37  */
     38 #define CODE_VALIDITY_PERIOD GNUNET_TIME_UNIT_MONTHS
     39 
     40 
     41 /**
     42  * Saves the State of a authorization plugin.
     43  */
     44 struct IBAN_Context
     45 {
     46 
     47   /**
     48    * Messages of the plugin, read from a resource file.
     49    */
     50   json_t *messages;
     51 
     52   /**
     53    * IBAN of our business, must be credited in the SEPA
     54    * wire transfer.
     55    */
     56   char *business_iban;
     57 
     58   /**
     59    * Name of our business, for the SEPA wire transfer.
     60    */
     61   char *business_name;
     62 
     63   /**
     64    * Handle to interact with a authorization backend.
     65    */
     66   const struct ANASTASIS_AuthorizationContext *ac;
     67 
     68   /**
     69    * Amount we expect to be transferred.
     70    */
     71   struct TALER_Amount expected_amount;
     72 
     73 };
     74 
     75 
     76 /**
     77  * Saves the State of a authorization process
     78  */
     79 struct ANASTASIS_AUTHORIZATION_State
     80 {
     81   /**
     82    * Public key of the challenge which is authorised
     83    */
     84   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     85 
     86   /**
     87    * Code which is sent to the user (here sent via IBAN)
     88    */
     89   uint64_t code;
     90 
     91   /**
     92    * Our plugin context.
     93    */
     94   struct IBAN_Context *ctx;
     95 
     96   /**
     97    * Function to call when we made progress.
     98    */
     99   GNUNET_SCHEDULER_TaskCallback trigger;
    100 
    101   /**
    102    * Closure for @e trigger.
    103    */
    104   void *trigger_cls;
    105 
    106   /**
    107    * holds the truth information
    108    */
    109   char *iban_number;
    110 
    111   /**
    112    * Our client connection, set if suspended.
    113    */
    114   struct MHD_Connection *connection;
    115 
    116   /**
    117    * Handler for database event we are waiting for.
    118    */
    119   struct GNUNET_DB_EventHandler *eh;
    120 
    121   /**
    122    * Amount that was transferred.
    123    */
    124   struct TALER_Amount amount;
    125 };
    126 
    127 
    128 /**
    129  * Obtain internationalized message @a msg_id from @a ctx using
    130  * language preferences of @a conn.
    131  *
    132  * @param messages JSON object to lookup message from
    133  * @param conn connection to lookup message for
    134  * @param msg_id unique message ID
    135  * @return NULL if message was not found
    136  */
    137 static const char *
    138 get_message (const json_t *messages,
    139              struct MHD_Connection *conn,
    140              const char *msg_id)
    141 {
    142   const char *accept_lang;
    143 
    144   accept_lang = MHD_lookup_connection_value (conn,
    145                                              MHD_HEADER_KIND,
    146                                              MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    147   if (NULL == accept_lang)
    148     accept_lang = "en_US";
    149   {
    150     const char *ret;
    151     struct GNUNET_JSON_Specification spec[] = {
    152       TALER_JSON_spec_i18n_string (msg_id,
    153                                    accept_lang,
    154                                    &ret),
    155       GNUNET_JSON_spec_end ()
    156     };
    157 
    158     if (GNUNET_OK !=
    159         GNUNET_JSON_parse (messages,
    160                            spec,
    161                            NULL, NULL))
    162     {
    163       GNUNET_break (0);
    164       GNUNET_JSON_parse_free (spec);
    165       return NULL;
    166     }
    167     GNUNET_JSON_parse_free (spec);
    168     return ret;
    169   }
    170 }
    171 
    172 
    173 /**
    174  * Validate @a data is a well-formed input into the challenge method,
    175  * i.e. @a data is a well-formed iban number for sending an IBAN, or
    176  * a well-formed e-mail address for sending an e-mail. Not expected to
    177  * check that the iban number or e-mail account actually exists.
    178  *
    179  * To be possibly used before issuing a 402 payment required to the client.
    180  *
    181  * @param cls closure with a `struct IBAN_Context`
    182  * @param connection HTTP client request (for queuing response)
    183  * @param truth_mime mime type of @e data
    184  * @param data input to validate (i.e. is it a valid iban number, etc.)
    185  * @param data_length number of bytes in @a data
    186  * @return #GNUNET_OK if @a data is valid,
    187  *         #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection
    188  *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection
    189  */
    190 static enum GNUNET_GenericReturnValue
    191 iban_validate (void *cls,
    192                struct MHD_Connection *connection,
    193                const char *truth_mime,
    194                const char *data,
    195                size_t data_length)
    196 {
    197   char *iban_number;
    198   char *emsg;
    199 
    200   iban_number = GNUNET_strndup (data,
    201                                 data_length);
    202   emsg = TALER_iban_validate (iban_number);
    203   if (NULL != emsg)
    204   {
    205     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    206                 "Invalid IBAN `%s' provided: %s\n",
    207                 iban_number,
    208                 emsg);
    209     GNUNET_free (iban_number);
    210     if (MHD_NO ==
    211         TALER_MHD_reply_with_error (connection,
    212                                     MHD_HTTP_CONFLICT,
    213                                     TALER_EC_ANASTASIS_IBAN_INVALID,
    214                                     emsg))
    215     {
    216       GNUNET_free (emsg);
    217       return GNUNET_SYSERR;
    218     }
    219     GNUNET_free (emsg);
    220     return GNUNET_NO;
    221   }
    222   GNUNET_free (iban_number);
    223   return GNUNET_OK;
    224 }
    225 
    226 
    227 /**
    228  * Begin issuing authentication challenge to user based on @a data.
    229  * Sends IBAN.
    230  *
    231  * @param cls closure with a `struct IBAN_Context`
    232  * @param trigger function to call when we made progress
    233  * @param trigger_cls closure for @a trigger
    234  * @param truth_uuid Identifier of the challenge, to be (if possible) included in the
    235  *             interaction with the user
    236  * @param code secret code that the user has to provide back to satisfy the challenge in
    237  *             the main anastasis protocol
    238  * @param data input to validate (i.e. is it a valid iban number, etc.)
    239  * @param data_length number of bytes in @a data
    240  * @return state to track progress on the authorization operation, NULL on failure
    241  */
    242 static struct ANASTASIS_AUTHORIZATION_State *
    243 iban_start (void *cls,
    244             GNUNET_SCHEDULER_TaskCallback trigger,
    245             void *trigger_cls,
    246             const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    247             uint64_t code,
    248             const void *data,
    249             size_t data_length)
    250 {
    251   struct IBAN_Context *ctx = cls;
    252   struct ANASTASIS_AUTHORIZATION_State *as;
    253 
    254   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
    255   as->trigger = trigger;
    256   as->trigger_cls = trigger_cls;
    257   as->ctx = ctx;
    258   as->truth_uuid = *truth_uuid;
    259   as->code = code;
    260   as->iban_number = GNUNET_strndup (data,
    261                                     data_length);
    262   return as;
    263 }
    264 
    265 
    266 /**
    267  * Function called when we received a wire transfer
    268  * with the respective code from the specified IBAN.
    269  *
    270  * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
    271  * @param extra string describing amount transferred
    272  * @param extra_size number of byes in @a extra
    273  */
    274 static void
    275 bank_event_cb (void *cls,
    276                const void *extra,
    277                size_t extra_size)
    278 {
    279   struct ANASTASIS_AUTHORIZATION_State *as = cls;
    280   char *amount_s;
    281 
    282   if (NULL != extra)
    283   {
    284     amount_s = GNUNET_strndup (extra,
    285                                extra_size);
    286     if (GNUNET_OK !=
    287         TALER_string_to_amount (amount_s,
    288                                 &as->amount))
    289     {
    290       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    291                   "Expected amount in event notification, got `%s'\n",
    292                   amount_s);
    293     }
    294     GNUNET_free (amount_s);
    295   }
    296   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    297               "IBAN event triggers resumption of request handling\n");
    298   MHD_resume_connection (as->connection);
    299   as->trigger (as->trigger_cls);
    300 }
    301 
    302 
    303 #include "iban.c"
    304 
    305 
    306 /**
    307  * Check if the @a wire_subject matches the challenge in the context
    308  * and if the @a amount is sufficient. If so, return true.
    309  *
    310  * @param cls a `const struct ANASTASIS_AUTHORIZATION_State *`
    311  * @param amount the amount that was transferred
    312  * @param wire_subject a wire subject we received
    313  * @return true if the wire transfer satisfied the check
    314  */
    315 static bool
    316 check_payment_ok (void *cls,
    317                   const struct TALER_Amount *amount,
    318                   const char *wire_subject)
    319 {
    320   const struct ANASTASIS_AUTHORIZATION_State *as = cls;
    321   struct IBAN_Context *ctx = as->ctx;
    322   uint64_t code;
    323   struct TALER_Amount camount;
    324 
    325   if (GNUNET_OK !=
    326       extract_code (wire_subject,
    327                     &code))
    328     return false;
    329   /* Database uses 'default' currency, but this
    330      plugin may use a different currency (and the
    331      same goes for the bank). So we fix this by
    332      forcing the currency to be 'right'. */
    333   camount = *amount;
    334   strcpy (camount.currency,
    335           ctx->expected_amount.currency);
    336   if (1 ==
    337       TALER_amount_cmp (&ctx->expected_amount,
    338                         &camount))
    339   {
    340     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    341                 "Amount `%s' insufficient for authorization\n",
    342                 TALER_amount2s (&camount));
    343     return false;
    344   }
    345   return (code == as->code);
    346 }
    347 
    348 
    349 /**
    350  * Check if we have received a wire transfer with a subject
    351  * authorizing the disclosure of the credential in the meantime.
    352  *
    353  * @param as state to check for
    354  * @return WTS_SUCCESS if a transfer was received,
    355  *         WTS_NOT_READY if no transfer was received,
    356  *         WTS_FAILED_WITH_REPLY if we had an internal error and queued a reply
    357  *         WTS_FAILED_WITHOUT_REPLY if we had an internal error and failed to queue a reply
    358  */
    359 static enum
    360 {
    361   WTS_SUCCESS,
    362   WTS_NOT_READY,
    363   WTS_FAILED_WITH_REPLY,
    364   WTS_FAILED_WITHOUT_REPLY
    365 }
    366 test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as)
    367 {
    368   struct IBAN_Context *ctx = as->ctx;
    369   struct ANASTASIS_DatabasePlugin *db = ctx->ac->db;
    370   enum GNUNET_DB_QueryStatus qs;
    371   struct GNUNET_TIME_Absolute now;
    372   struct GNUNET_TIME_Timestamp limit;
    373 
    374   now = GNUNET_TIME_absolute_get ();
    375   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    376               "Testing for wire transfers\n");
    377   limit = GNUNET_TIME_absolute_to_timestamp (
    378     GNUNET_TIME_absolute_subtract (now,
    379                                    CODE_VALIDITY_PERIOD));
    380   qs = db->test_auth_iban_payment (
    381     db->cls,
    382     as->iban_number,
    383     limit,
    384     &check_payment_ok,
    385     as);
    386   switch (qs)
    387   {
    388   case GNUNET_DB_STATUS_HARD_ERROR:
    389   case GNUNET_DB_STATUS_SOFT_ERROR:
    390     return (MHD_YES ==
    391             TALER_MHD_reply_with_error (as->connection,
    392                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    393                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
    394                                         NULL))
    395            ? WTS_FAILED_WITH_REPLY
    396            : WTS_FAILED_WITHOUT_REPLY;
    397   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    398     return WTS_NOT_READY;
    399   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    400     break;
    401   }
    402   qs = db->mark_challenge_code_satisfied (
    403     db->cls,
    404     &as->truth_uuid,
    405     as->code);
    406   GNUNET_break (qs > 0);
    407   return WTS_SUCCESS;
    408 }
    409 
    410 
    411 /**
    412  * Respond with instructions to the user how to
    413  * satisfy the challenge.
    414  *
    415  * @param as authorization state
    416  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    417  * @return state of the request
    418  */
    419 static enum ANASTASIS_AUTHORIZATION_ChallengeResult
    420 iban_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
    421                 struct MHD_Connection *connection)
    422 {
    423   struct IBAN_Context *ctx = as->ctx;
    424   const char *mime;
    425   const char *lang;
    426   MHD_RESULT mres;
    427 
    428   mime = MHD_lookup_connection_value (connection,
    429                                       MHD_HEADER_KIND,
    430                                       MHD_HTTP_HEADER_ACCEPT);
    431   if (NULL == mime)
    432     mime = "text/plain";
    433   lang = MHD_lookup_connection_value (connection,
    434                                       MHD_HEADER_KIND,
    435                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    436   if (NULL == lang)
    437     lang = "en";
    438 
    439   /* Build HTTP response */
    440   {
    441     struct MHD_Response *resp;
    442 
    443     if (0.0 < TALER_pattern_matches (mime,
    444                                      "application/json"))
    445     {
    446       char subject[64];
    447 
    448       GNUNET_snprintf (subject,
    449                        sizeof (subject),
    450                        "Anastasis %llu",
    451                        (unsigned long long) as->code);
    452       resp = TALER_MHD_MAKE_JSON_PACK (
    453         GNUNET_JSON_pack_string ("challenge_type",
    454                                  "IBAN_WIRE"),
    455         GNUNET_JSON_pack_object_steal (
    456           "wire_details",
    457           GNUNET_JSON_PACK (
    458             GNUNET_JSON_pack_uint64 (
    459               "answer_code",
    460               as->code),
    461             TALER_JSON_pack_amount (
    462               "challenge_amount",
    463               &ctx->expected_amount),
    464             GNUNET_JSON_pack_string (
    465               "credit_iban",
    466               ctx->business_iban),
    467             GNUNET_JSON_pack_string (
    468               "business_name",
    469               ctx->business_name),
    470             GNUNET_JSON_pack_string (
    471               "wire_transfer_subject",
    472               subject))));
    473     }
    474     else
    475     {
    476       size_t reply_len;
    477       char *reply;
    478 
    479       reply_len = GNUNET_asprintf (&reply,
    480                                    get_message (ctx->messages,
    481                                                 connection,
    482                                                 "instructions"),
    483                                    TALER_amount2s (&ctx->expected_amount),
    484                                    ctx->business_name,
    485                                    ctx->business_iban,
    486                                    (unsigned long long) as->code);
    487       resp = MHD_create_response_from_buffer (reply_len,
    488                                               reply,
    489                                               MHD_RESPMEM_MUST_COPY);
    490       GNUNET_free (reply);
    491       TALER_MHD_add_global_headers (resp,
    492                                     false);
    493       GNUNET_break (MHD_YES ==
    494                     MHD_add_response_header (resp,
    495                                              MHD_HTTP_HEADER_CONTENT_TYPE,
    496                                              "text/plain"));
    497     }
    498     mres = MHD_queue_response (connection,
    499                                MHD_HTTP_OK,
    500                                resp);
    501     MHD_destroy_response (resp);
    502     if (MHD_YES != mres)
    503       return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED;
    504     return ANASTASIS_AUTHORIZATION_CRES_SUCCESS;
    505   }
    506 }
    507 
    508 
    509 /**
    510  * Begin issuing authentication challenge to user based on @a data.
    511  * I.e. start to send IBAN or e-mail or launch video identification.
    512  *
    513  * @param as authorization state
    514  * @param timeout how long do we have to produce a reply
    515  * @param challenge_response hash of the challenge response, or NULL
    516  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    517  * @return state of the request
    518  */
    519 static enum ANASTASIS_AUTHORIZATION_SolveResult
    520 iban_solve (struct ANASTASIS_AUTHORIZATION_State *as,
    521             struct GNUNET_TIME_Absolute timeout,
    522             const struct GNUNET_HashCode *challenge_response,
    523             struct MHD_Connection *connection)
    524 {
    525   struct IBAN_Context *ctx = as->ctx;
    526   struct ANASTASIS_DatabasePlugin *db = ctx->ac->db;
    527   MHD_RESULT mres;
    528   enum GNUNET_DB_QueryStatus qs;
    529   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    530   struct GNUNET_TIME_Timestamp after;
    531 
    532   if (NULL == as->eh)
    533   {
    534     struct IbanEventP espec = {
    535       .header.size = htons (sizeof (espec)),
    536       .header.type = htons (TALER_DBEVENT_ANASTASIS_AUTH_IBAN_TRANSFER),
    537       .code = GNUNET_htonll (as->code)
    538     };
    539 
    540     GNUNET_CRYPTO_hash (as->iban_number,
    541                         strlen (as->iban_number),
    542                         &espec.debit_iban_hash);
    543     as->eh = db->event_listen (db->cls,
    544                                &espec.header,
    545                                GNUNET_TIME_absolute_get_remaining (
    546                                  timeout),
    547                                &bank_event_cb,
    548                                as);
    549   }
    550   after = GNUNET_TIME_absolute_to_timestamp (
    551     GNUNET_TIME_absolute_subtract (now,
    552                                    CODE_VALIDITY_PERIOD));
    553   qs = db->test_challenge_code_satisfied (db->cls,
    554                                           &as->truth_uuid,
    555                                           as->code,
    556                                           after);
    557   switch (qs)
    558   {
    559   case GNUNET_DB_STATUS_HARD_ERROR:
    560   case GNUNET_DB_STATUS_SOFT_ERROR:
    561     mres = TALER_MHD_reply_with_error (connection,
    562                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    563                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
    564                                        "test challenge code satisfied");
    565     if (MHD_YES != mres)
    566       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    567     return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    568   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    569     switch (test_wire_transfers (as))
    570     {
    571     case WTS_SUCCESS:
    572       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    573                   "IBAN authorization finished!\n");
    574       return ANASTASIS_AUTHORIZATION_SRES_FINISHED;
    575     case WTS_NOT_READY:
    576       break;   /* continue below */
    577     case WTS_FAILED_WITH_REPLY:
    578       return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    579     case WTS_FAILED_WITHOUT_REPLY:
    580       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    581     }
    582     if (GNUNET_TIME_absolute_is_future (timeout))
    583     {
    584       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    585                   "Suspending IBAN check until %s\n",
    586                   GNUNET_TIME_absolute2s (timeout));
    587       as->connection = connection;
    588       MHD_suspend_connection (connection);
    589       return ANASTASIS_AUTHORIZATION_SRES_SUSPENDED;
    590     }
    591     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    592                 "Timeout reached at %s, failing request\n",
    593                 GNUNET_TIME_absolute2s (timeout));
    594     mres = TALER_MHD_reply_with_error (connection,
    595                                        MHD_HTTP_FORBIDDEN,
    596                                        TALER_EC_ANASTASIS_IBAN_MISSING_TRANSFER,
    597                                        NULL);
    598     if (MHD_YES != mres)
    599       return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    600     return ANASTASIS_AUTHORIZATION_SRES_FAILED;
    601   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    602     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    603                 "IBAN authorization finished!\n");
    604     return ANASTASIS_AUTHORIZATION_SRES_FINISHED;
    605   }
    606   /* should be impossible */
    607   GNUNET_break (0);
    608   return ANASTASIS_AUTHORIZATION_SRES_FAILED_REPLY_FAILED;
    609 }
    610 
    611 
    612 /**
    613  * Free internal state associated with @a as.
    614  *
    615  * @param as state to clean up
    616  */
    617 static void
    618 iban_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
    619 {
    620   struct IBAN_Context *ctx = as->ctx;
    621 
    622   if (NULL != as->eh)
    623   {
    624     ctx->ac->db->event_listen_cancel (as->eh);
    625     as->eh = NULL;
    626   }
    627   GNUNET_free (as->iban_number);
    628   GNUNET_free (as);
    629 }
    630 
    631 
    632 /**
    633  * Initialize email based authorization plugin
    634  *
    635  * @param cls a `struct ANASTASIS_AuthorizationContext`
    636  * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
    637  */
    638 void *
    639 libanastasis_plugin_authorization_iban_init (void *cls);
    640 
    641 /* declaration to fix compiler warning */
    642 void *
    643 libanastasis_plugin_authorization_iban_init (void *cls)
    644 {
    645   struct ANASTASIS_AuthorizationContext *ac = cls;
    646   struct ANASTASIS_AuthorizationPlugin *plugin;
    647   const struct GNUNET_CONFIGURATION_Handle *cfg = ac->cfg;
    648   struct IBAN_Context *ctx;
    649 
    650   ctx = GNUNET_new (struct IBAN_Context);
    651   if (GNUNET_OK !=
    652       GNUNET_CONFIGURATION_get_value_string (cfg,
    653                                              "authorization-iban",
    654                                              "CREDIT_IBAN",
    655                                              &ctx->business_iban))
    656   {
    657     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    658                                "authorization-iban",
    659                                "CREDIT_IBAN");
    660     GNUNET_free (ctx);
    661     return NULL;
    662   }
    663   if (GNUNET_OK !=
    664       TALER_config_get_amount (cfg,
    665                                "authorization-iban",
    666                                "COST",
    667                                &ctx->expected_amount))
    668   {
    669     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    670                                "authorization-iban",
    671                                "COST");
    672     GNUNET_free (ctx);
    673     return NULL;
    674   }
    675   if (GNUNET_OK !=
    676       GNUNET_CONFIGURATION_get_value_string (cfg,
    677                                              "authorization-iban",
    678                                              "BUSINESS_NAME",
    679                                              &ctx->business_name))
    680   {
    681     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    682                                "authorization-iban",
    683                                "BUSINESS_NAME");
    684     GNUNET_free (ctx->business_iban);
    685     GNUNET_free (ctx);
    686     return NULL;
    687   }
    688   {
    689     char *fn;
    690     json_error_t err;
    691     char *tmp;
    692 
    693     tmp = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    694                                            GNUNET_OS_IPK_DATADIR);
    695     GNUNET_asprintf (&fn,
    696                      "%sauthorization-iban-messages.json",
    697                      tmp);
    698     GNUNET_free (tmp);
    699     ctx->messages = json_load_file (fn,
    700                                     JSON_REJECT_DUPLICATES,
    701                                     &err);
    702     if (NULL == ctx->messages)
    703     {
    704       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    705                   "Failed to load messages from `%s': %s at %d:%d\n",
    706                   fn,
    707                   err.text,
    708                   err.line,
    709                   err.column);
    710       GNUNET_free (ctx->business_iban);
    711       GNUNET_free (ctx->business_name);
    712       GNUNET_free (fn);
    713       GNUNET_free (ctx);
    714       return NULL;
    715     }
    716     GNUNET_free (fn);
    717   }
    718   ctx->ac = ac;
    719   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
    720   plugin->payment_plugin_managed = true;
    721   plugin->retry_counter = UINT32_MAX; /* long polling */
    722   plugin->code_validity_period = CODE_VALIDITY_PERIOD;
    723   plugin->code_rotation_period = GNUNET_TIME_UNIT_ZERO;
    724   plugin->code_retransmission_frequency = GNUNET_TIME_UNIT_ZERO; /* not applicable */
    725   plugin->cls = ctx;
    726   plugin->validate = &iban_validate;
    727   plugin->start = &iban_start;
    728   plugin->challenge = &iban_challenge;
    729   plugin->solve = &iban_solve;
    730   plugin->cleanup = &iban_cleanup;
    731   return plugin;
    732 }
    733 
    734 
    735 /**
    736  * Unload authorization plugin
    737  *
    738  * @param cls a `struct ANASTASIS_AuthorizationPlugin`
    739  * @return NULL (always)
    740  */
    741 void *
    742 libanastasis_plugin_authorization_iban_done (void *cls);
    743 
    744 /* declaration to fix compiler warning */
    745 void *
    746 libanastasis_plugin_authorization_iban_done (void *cls)
    747 {
    748   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
    749   struct IBAN_Context *ctx = plugin->cls;
    750 
    751   json_decref (ctx->messages);
    752   GNUNET_free (ctx->business_iban);
    753   GNUNET_free (ctx->business_name);
    754   GNUNET_free (ctx);
    755   GNUNET_free (plugin);
    756   return NULL;
    757 }