anastasis

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

anastasis_authorization_plugin_post.c (23961B)


      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_post.c
     18  * @brief authorization plugin post 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 <jansson.h>
     26 #include "anastasis_util_lib.h"
     27 #include <gnunet/gnunet_db_lib.h>
     28 #include "anastasis_database_lib.h"
     29 
     30 /**
     31  * How many retries do we allow per code?
     32  */
     33 #define INITIAL_RETRY_COUNTER 3
     34 
     35 
     36 /**
     37  * Saves the State of a authorization plugin.
     38  */
     39 struct PostContext
     40 {
     41 
     42   /**
     43    * Command which is executed to run the plugin (some bash script or a
     44    * command line argument)
     45    */
     46   char *auth_command;
     47 
     48   /**
     49    * Messages of the plugin, read from a resource file.
     50    */
     51   json_t *messages;
     52 
     53   /**
     54    * Argument passed to the "init" function of each
     55    * plugin.
     56    */
     57   const struct ANASTASIS_AuthorizationContext *ac;
     58 };
     59 
     60 
     61 /**
     62  * Saves the state of a authorization process
     63  */
     64 struct ANASTASIS_AUTHORIZATION_State
     65 {
     66   /**
     67    * Public key of the challenge which is authorised
     68    */
     69   struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
     70 
     71   /**
     72    * Code which is sent to the user.
     73    */
     74   uint64_t code;
     75 
     76   /**
     77    * Our plugin context.
     78    */
     79   struct PostContext *ctx;
     80 
     81   /**
     82    * Function to call when we made progress.
     83    */
     84   GNUNET_SCHEDULER_TaskCallback trigger;
     85 
     86   /**
     87    * Closure for @e trigger.
     88    */
     89   void *trigger_cls;
     90 
     91   /**
     92    * holds the truth information
     93    */
     94   json_t *post;
     95 
     96   /**
     97    * Handle to the helper process.
     98    */
     99   struct GNUNET_Process *child;
    100 
    101   /**
    102    * Handle to wait for @e child
    103    */
    104   struct GNUNET_ChildWaitHandle *cwh;
    105 
    106   /**
    107    * Our client connection, set if suspended.
    108    */
    109   struct MHD_Connection *connection;
    110 
    111   /**
    112    * Message to send.
    113    */
    114   char *msg;
    115 
    116   /**
    117    * Offset of transmission in msg.
    118    */
    119   size_t msg_off;
    120 
    121   /**
    122    * Exit code from helper.
    123    */
    124   long unsigned int exit_code;
    125 
    126   /**
    127    * How did the helper die?
    128    */
    129   enum GNUNET_OS_ProcessStatusType pst;
    130 
    131 
    132 };
    133 
    134 
    135 /**
    136  * Returned by #get_message() when the configured messages file has no entry
    137  * for the requested ID.  This is an installation error, but it must not be
    138  * allowed to reach GNUNET_asprintf() as a NULL format string.  The text
    139  * deliberately contains no printf conversions, so substituting it for any
    140  * template is safe whatever argument list the caller passes.
    141  */
    142 #define MISSING_MESSAGE \
    143         "The provider is misconfigured: a message template is missing."
    144 
    145 
    146 /**
    147  * Obtain internationalized message @a msg_id from @a ctx using
    148  * language preferences of @a conn.
    149  *
    150  * @param messages JSON object to lookup message from
    151  * @param conn connection to lookup message for
    152  * @param msg_id unique message ID
    153  * @return the requested message, or #MISSING_MESSAGE if it was not
    154  *         configured; never NULL
    155  */
    156 static const char *
    157 get_message (const json_t *messages,
    158              struct MHD_Connection *conn,
    159              const char *msg_id)
    160 {
    161   const char *accept_lang;
    162 
    163   accept_lang = MHD_lookup_connection_value (conn,
    164                                              MHD_HEADER_KIND,
    165                                              MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    166   if (NULL == accept_lang)
    167     accept_lang = "en_US";
    168   {
    169     const char *ret;
    170     struct GNUNET_JSON_Specification spec[] = {
    171       TALER_JSON_spec_i18n_string (msg_id,
    172                                    accept_lang,
    173                                    &ret),
    174       GNUNET_JSON_spec_end ()
    175     };
    176 
    177     if (GNUNET_OK !=
    178         GNUNET_JSON_parse (messages,
    179                            spec,
    180                            NULL, NULL))
    181     {
    182       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    183                   "Message `%s' is missing from the configured messages file\n",
    184                   msg_id);
    185       GNUNET_JSON_parse_free (spec);
    186       return MISSING_MESSAGE;
    187     }
    188     GNUNET_JSON_parse_free (spec);
    189     if (NULL == ret)
    190     {
    191       /* The parser of TALER_JSON_spec_i18n_string returns #GNUNET_OK even
    192          when the field is absent or is not a string, in which case it stores
    193          NULL; the check above therefore never fires for a missing message and
    194          this one is what keeps NULL out of the format argument of the
    195          GNUNET_asprintf() calls below. */
    196       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    197                   "Message `%s' is missing or not a string in the configured messages file\n",
    198                   msg_id);
    199       return MISSING_MESSAGE;
    200     }
    201     return ret;
    202   }
    203 }
    204 
    205 
    206 /**
    207  * Check that @a value cannot be mistaken for a command-line option.  The
    208  * address fields are handed to the operator-supplied COMMAND as positional
    209  * arguments, and we do not know whether that helper uses an option parser or
    210  * honours "--", so we refuse anything option-shaped rather than rely on it.
    211  *
    212  * @param value field value to check, may be NULL
    213  * @return true if @a value is safe to pass as a positional argument
    214  */
    215 static bool
    216 is_safe_argument (const char *value)
    217 {
    218   return ( (NULL != value) &&
    219            ('-' != value[0]) );
    220 }
    221 
    222 
    223 /**
    224  * Validate @a data is a well-formed input into the challenge method,
    225  * i.e. @a data is a well-formed phone number for sending an SMS, or
    226  * a well-formed e-mail address for sending an e-mail. Not expected to
    227  * check that the phone number or e-mail account actually exists.
    228  *
    229  * To be possibly used before issuing a 402 payment required to the client.
    230  *
    231  * @param cls closure
    232  * @param connection HTTP client request (for queuing response)
    233  * @param mime_type mime type of @e data
    234  * @param data input to validate (i.e. is it a valid phone number, etc.)
    235  * @param data_length number of bytes in @a data
    236  * @return #GNUNET_OK if @a data is valid,
    237  *         #GNUNET_NO if @a data is invalid and a reply was successfully queued on @a connection
    238  *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on @a connection
    239  */
    240 static enum GNUNET_GenericReturnValue
    241 post_validate (void *cls,
    242                struct MHD_Connection *connection,
    243                const char *mime_type,
    244                const char *data,
    245                size_t data_length)
    246 {
    247   struct PostContext *ctx = cls;
    248   json_t *j;
    249   json_error_t error;
    250   const char *name;
    251   const char *street;
    252   const char *city;
    253   const char *zip;
    254   const char *country;
    255   struct GNUNET_JSON_Specification spec[] = {
    256     GNUNET_JSON_spec_string ("full_name",
    257                              &name),
    258     GNUNET_JSON_spec_string ("street",
    259                              &street),
    260     GNUNET_JSON_spec_string ("city",
    261                              &city),
    262     GNUNET_JSON_spec_string ("postcode",
    263                              &zip),
    264     GNUNET_JSON_spec_string ("country",
    265                              &country),
    266     GNUNET_JSON_spec_end ()
    267   };
    268 
    269   (void) ctx;
    270   j = json_loadb (data,
    271                   data_length,
    272                   JSON_REJECT_DUPLICATES,
    273                   &error);
    274   if (NULL == j)
    275   {
    276     if (MHD_NO ==
    277         TALER_MHD_reply_with_error (connection,
    278                                     MHD_HTTP_CONFLICT,
    279                                     TALER_EC_ANASTASIS_POST_INVALID,
    280                                     "JSON malformed"))
    281       return GNUNET_SYSERR;
    282     return GNUNET_NO;
    283   }
    284 
    285   if (GNUNET_OK !=
    286       GNUNET_JSON_parse (j,
    287                          spec,
    288                          NULL, NULL))
    289   {
    290     GNUNET_break (0);
    291     json_decref (j);
    292     if (MHD_NO ==
    293         TALER_MHD_reply_with_error (connection,
    294                                     MHD_HTTP_CONFLICT,
    295                                     TALER_EC_ANASTASIS_POST_INVALID,
    296                                     "JSON lacked required address information"))
    297       return GNUNET_SYSERR;
    298     return GNUNET_NO;
    299   }
    300   if ( (! is_safe_argument (name)) ||
    301        (! is_safe_argument (street)) ||
    302        (! is_safe_argument (city)) ||
    303        (! is_safe_argument (zip)) ||
    304        (! is_safe_argument (country)) )
    305   {
    306     json_decref (j);
    307     if (MHD_NO ==
    308         TALER_MHD_reply_with_error (connection,
    309                                     MHD_HTTP_CONFLICT,
    310                                     TALER_EC_ANASTASIS_POST_INVALID,
    311                                     "address fields must not begin with `-'"))
    312       return GNUNET_SYSERR;
    313     return GNUNET_NO;
    314   }
    315   json_decref (j);
    316   return GNUNET_OK;
    317 }
    318 
    319 
    320 /**
    321  * Begin issuing authentication challenge to user based on @a data.
    322  * I.e. start to send mail.
    323  *
    324  * @param cls closure
    325  * @param trigger function to call when we made progress
    326  * @param trigger_cls closure for @a trigger
    327  * @param truth_uuid Identifier of the challenge, to be (if possible) included in the
    328  *             interaction with the user
    329  * @param code secret code that the user has to provide back to satisfy the challenge in
    330  *             the main anastasis protocol
    331  * @param data input to validate (i.e. is it a valid phone number, etc.)
    332  * @param data_length number of bytes in @a data
    333  * @return state to track progress on the authorization operation, NULL on failure
    334  */
    335 static struct ANASTASIS_AUTHORIZATION_State *
    336 post_start (void *cls,
    337             GNUNET_SCHEDULER_TaskCallback trigger,
    338             void *trigger_cls,
    339             const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
    340             uint64_t code,
    341             const void *data,
    342             size_t data_length)
    343 {
    344   struct PostContext *ctx = cls;
    345   struct ANASTASIS_AUTHORIZATION_State *as;
    346   json_error_t error;
    347   enum GNUNET_DB_QueryStatus qs;
    348 
    349   /* If the user can show this challenge code, this
    350      plugin is already happy (no additional
    351      requirements), so mark this challenge as
    352      already satisfied from the start. */
    353   qs = ANASTASIS_DB_update_to_challenge_code_satisfied (
    354     truth_uuid,
    355     code);
    356   if (qs <= 0)
    357   {
    358     GNUNET_break (0);
    359     return NULL;
    360   }
    361   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
    362   as->trigger = trigger;
    363   as->trigger_cls = trigger_cls;
    364   as->ctx = ctx;
    365   as->truth_uuid = *truth_uuid;
    366   as->code = code;
    367   as->post = json_loadb (data,
    368                          data_length,
    369                          JSON_REJECT_DUPLICATES,
    370                          &error);
    371   if (NULL == as->post)
    372   {
    373     GNUNET_break (0);
    374     GNUNET_free (as);
    375     return NULL;
    376   }
    377   return as;
    378 }
    379 
    380 
    381 /**
    382  * Function called when our Post helper has terminated.
    383  *
    384  * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
    385  * @param type type of the process
    386  * @param exit_code status code of the process
    387  */
    388 static void
    389 post_done_cb (void *cls,
    390               enum GNUNET_OS_ProcessStatusType type,
    391               long unsigned int exit_code)
    392 {
    393   struct ANASTASIS_AUTHORIZATION_State *as = cls;
    394 
    395   as->cwh = NULL;
    396   if (NULL != as->child)
    397   {
    398     GNUNET_process_destroy (as->child);
    399     as->child = NULL;
    400   }
    401   as->pst = type;
    402   as->exit_code = exit_code;
    403   MHD_resume_connection (as->connection);
    404   as->trigger (as->trigger_cls);
    405 }
    406 
    407 
    408 /**
    409  * Begin issuing authentication challenge to user based on @a data.
    410  * I.e. start to send SMS or e-mail or launch video identification.
    411  *
    412  * @param as authorization state
    413  * @param connection HTTP client request (for queuing response, such as redirection to video portal)
    414  * @return state of the request
    415  */
    416 static enum ANASTASIS_AUTHORIZATION_ChallengeResult
    417 post_challenge (struct ANASTASIS_AUTHORIZATION_State *as,
    418                 struct MHD_Connection *connection)
    419 {
    420   const char *mime;
    421   const char *lang;
    422   enum MHD_Result mres;
    423   const char *name;
    424   const char *street;
    425   const char *city;
    426   const char *zip;
    427   const char *country;
    428   struct GNUNET_JSON_Specification spec[] = {
    429     GNUNET_JSON_spec_string ("full_name",
    430                              &name),
    431     GNUNET_JSON_spec_string ("street",
    432                              &street),
    433     GNUNET_JSON_spec_string ("city",
    434                              &city),
    435     GNUNET_JSON_spec_string ("postcode",
    436                              &zip),
    437     GNUNET_JSON_spec_string ("country",
    438                              &country),
    439     GNUNET_JSON_spec_end ()
    440   };
    441 
    442   mime = MHD_lookup_connection_value (connection,
    443                                       MHD_HEADER_KIND,
    444                                       MHD_HTTP_HEADER_ACCEPT);
    445   if (NULL == mime)
    446     mime = "text/plain";
    447   lang = MHD_lookup_connection_value (connection,
    448                                       MHD_HEADER_KIND,
    449                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    450   if (NULL == lang)
    451     lang = "en";
    452   if (GNUNET_OK !=
    453       GNUNET_JSON_parse (as->post,
    454                          spec,
    455                          NULL, NULL))
    456   {
    457     GNUNET_break (0);
    458     mres = TALER_MHD_reply_with_error (connection,
    459                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    460                                        TALER_EC_ANASTASIS_POST_INVALID,
    461                                        "address information incomplete");
    462     if (MHD_YES != mres)
    463       return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    464     return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    465   }
    466   /* Re-checked here and not only in #post_validate(), because truths stored
    467      before that check existed would otherwise still reach the helper. */
    468   if ( (! is_safe_argument (name)) ||
    469        (! is_safe_argument (street)) ||
    470        (! is_safe_argument (city)) ||
    471        (! is_safe_argument (zip)) ||
    472        (! is_safe_argument (country)) )
    473   {
    474     GNUNET_break_op (0);
    475     mres = TALER_MHD_reply_with_error (connection,
    476                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    477                                        TALER_EC_ANASTASIS_POST_INVALID,
    478                                        "address fields must not begin with `-'")
    479     ;
    480     if (MHD_YES != mres)
    481       return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    482     return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    483   }
    484   if (NULL == as->msg)
    485   {
    486     /* First time, start child process and feed pipe */
    487     struct GNUNET_DISK_PipeHandle *p;
    488     struct GNUNET_DISK_FileHandle *pipe_stdin;
    489 
    490     p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
    491     if (NULL == p)
    492     {
    493       mres = TALER_MHD_reply_with_error (connection,
    494                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    495                                          TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
    496                                          "pipe");
    497       if (MHD_YES != mres)
    498         return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    499       return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    500     }
    501     as->child  = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
    502     GNUNET_assert (GNUNET_OK ==
    503                    GNUNET_process_set_options (
    504                      as->child,
    505                      GNUNET_process_option_inherit_rpipe (p,
    506                                                           STDIN_FILENO)));
    507     if (GNUNET_OK !=
    508         GNUNET_process_run_command_va (as->child,
    509                                        as->ctx->auth_command,
    510                                        as->ctx->auth_command,
    511                                        name,
    512                                        street,
    513                                        city,
    514                                        zip,
    515                                        country,
    516                                        NULL))
    517     {
    518       GNUNET_process_destroy (as->child);
    519       as->child = NULL;
    520       GNUNET_DISK_pipe_close (p);
    521       mres = TALER_MHD_reply_with_error (connection,
    522                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    523                                          TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
    524                                          "exec");
    525       if (MHD_YES != mres)
    526         return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    527       return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    528     }
    529     pipe_stdin = GNUNET_DISK_pipe_detach_end (p,
    530                                               GNUNET_DISK_PIPE_END_WRITE);
    531     GNUNET_assert (NULL != pipe_stdin);
    532     GNUNET_DISK_pipe_close (p);
    533     GNUNET_asprintf (&as->msg,
    534                      get_message (as->ctx->messages,
    535                                   connection,
    536                                   "body"),
    537                      ANASTASIS_pin2s (as->code),
    538                      ANASTASIS_CRYPTO_uuid2s (&as->truth_uuid));
    539     {
    540       const char *off = as->msg;
    541       size_t left = strlen (off);
    542 
    543       while (0 != left)
    544       {
    545         ssize_t ret;
    546 
    547         ret = GNUNET_DISK_file_write (pipe_stdin,
    548                                       off,
    549                                       left);
    550         if (ret <= 0)
    551         {
    552           mres = TALER_MHD_reply_with_error (connection,
    553                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    554                                              TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
    555                                              "write");
    556           if (MHD_YES != mres)
    557             return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    558           return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    559         }
    560         as->msg_off += ret;
    561         off += ret;
    562         left -= ret;
    563       }
    564       GNUNET_DISK_file_close (pipe_stdin);
    565     }
    566     as->cwh = GNUNET_wait_child (as->child,
    567                                  &post_done_cb,
    568                                  as);
    569     as->connection = connection;
    570     MHD_suspend_connection (connection);
    571     return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED;
    572   }
    573   if (NULL != as->cwh)
    574   {
    575     /* Spurious call, why are we here? */
    576     GNUNET_break (0);
    577     MHD_suspend_connection (connection);
    578     return ANASTASIS_AUTHORIZATION_CRES_SUSPENDED;
    579   }
    580   if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
    581        (0 != as->exit_code) )
    582   {
    583     char es[32];
    584 
    585     GNUNET_snprintf (es,
    586                      sizeof (es),
    587                      "%u/%d",
    588                      (unsigned int) as->exit_code,
    589                      as->pst);
    590     mres = TALER_MHD_reply_with_error (connection,
    591                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    592                                        TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED,
    593                                        es);
    594     if (MHD_YES != mres)
    595       return ANASTASIS_AUTHORIZATION_CRES_FAILED_REPLY_FAILED;
    596     return ANASTASIS_AUTHORIZATION_CRES_FAILED;
    597   }
    598 
    599   /* Build HTTP response */
    600   {
    601     struct MHD_Response *resp;
    602 
    603     if (0.0 < TALER_pattern_matches (mime,
    604                                      "application/json"))
    605     {
    606       resp = TALER_MHD_MAKE_JSON_PACK (
    607         GNUNET_JSON_pack_string ("challenge_type",
    608                                  "TAN_SENT"),
    609         GNUNET_JSON_pack_string ("tan_address_hint",
    610                                  zip));
    611     }
    612     else
    613     {
    614       size_t reply_len;
    615       char *reply;
    616 
    617       reply_len = GNUNET_asprintf (&reply,
    618                                    get_message (as->ctx->messages,
    619                                                 connection,
    620                                                 "instructions"),
    621                                    zip);
    622       resp = MHD_create_response_from_buffer (reply_len,
    623                                               reply,
    624                                               MHD_RESPMEM_MUST_COPY);
    625       GNUNET_free (reply);
    626       TALER_MHD_add_global_headers (resp,
    627                                     false);
    628     }
    629     mres = MHD_queue_response (connection,
    630                                MHD_HTTP_OK,
    631                                resp);
    632     MHD_destroy_response (resp);
    633     if (MHD_YES != mres)
    634       return ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED;
    635     return ANASTASIS_AUTHORIZATION_CRES_SUCCESS;
    636   }
    637 }
    638 
    639 
    640 /**
    641  * Free internal state associated with @a as.
    642  *
    643  * @param as state to clean up
    644  */
    645 static void
    646 post_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
    647 {
    648   if (NULL != as->cwh)
    649   {
    650     GNUNET_wait_child_cancel (as->cwh);
    651     as->cwh = NULL;
    652   }
    653   if (NULL != as->child)
    654   {
    655     GNUNET_break (GNUNET_OK ==
    656                   GNUNET_process_kill (as->child,
    657                                        SIGKILL));
    658     GNUNET_break (GNUNET_OK ==
    659                   GNUNET_process_wait (as->child,
    660                                        true,
    661                                        NULL,
    662                                        NULL));
    663     GNUNET_process_destroy (as->child);
    664     as->child = NULL;
    665   }
    666   GNUNET_free (as->msg);
    667   json_decref (as->post);
    668   GNUNET_free (as);
    669 }
    670 
    671 
    672 /**
    673  * Initialize post based authorization plugin
    674  *
    675  * @param cls a configuration instance
    676  * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
    677  */
    678 void *
    679 libanastasis_plugin_authorization_post_init (void *cls);
    680 
    681 /* declaration to fix compiler warning */
    682 void *
    683 libanastasis_plugin_authorization_post_init (void *cls)
    684 {
    685   const struct ANASTASIS_AuthorizationContext *ac = cls;
    686   struct ANASTASIS_AuthorizationPlugin *plugin;
    687   const struct GNUNET_CONFIGURATION_Handle *cfg = ac->cfg;
    688   struct PostContext *ctx;
    689 
    690   ctx = GNUNET_new (struct PostContext);
    691   ctx->ac = ac;
    692   {
    693     char *fn;
    694     json_error_t err;
    695     char *tmp;
    696 
    697     tmp = GNUNET_OS_installation_get_path (ANASTASIS_project_data (),
    698                                            GNUNET_OS_IPK_DATADIR);
    699     GNUNET_asprintf (&fn,
    700                      "%sauthorization-post-messages.json",
    701                      tmp);
    702     GNUNET_free (tmp);
    703     ctx->messages = json_load_file (fn,
    704                                     JSON_REJECT_DUPLICATES,
    705                                     &err);
    706     if (NULL == ctx->messages)
    707     {
    708       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    709                   "Failed to load messages from `%s': %s at %d:%d\n",
    710                   fn,
    711                   err.text,
    712                   err.line,
    713                   err.column);
    714       GNUNET_free (fn);
    715       GNUNET_free (ctx);
    716       return NULL;
    717     }
    718     GNUNET_free (fn);
    719   }
    720   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
    721   plugin->retry_counter = INITIAL_RETRY_COUNTER;
    722   plugin->code_validity_period = GNUNET_TIME_UNIT_MONTHS;
    723   plugin->code_rotation_period = GNUNET_TIME_UNIT_WEEKS;
    724   plugin->code_retransmission_frequency
    725     = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
    726                                      2);
    727   plugin->cls = ctx;
    728   plugin->validate = &post_validate;
    729   plugin->start = &post_start;
    730   plugin->challenge = &post_challenge;
    731   plugin->cleanup = &post_cleanup;
    732 
    733   if (GNUNET_OK !=
    734       GNUNET_CONFIGURATION_get_value_string (cfg,
    735                                              "authorization-post",
    736                                              "COMMAND",
    737                                              &ctx->auth_command))
    738   {
    739     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    740                                "authorization-post",
    741                                "COMMAND");
    742     json_decref (ctx->messages);
    743     GNUNET_free (ctx);
    744     GNUNET_free (plugin);
    745     return NULL;
    746   }
    747   return plugin;
    748 }
    749 
    750 
    751 /**
    752  * Unload authorization plugin
    753  *
    754  * @param cls a `struct ANASTASIS_AuthorizationPlugin`
    755  * @return NULL (always)
    756  */
    757 void *
    758 libanastasis_plugin_authorization_post_done (void *cls);
    759 
    760 /* declaration to fix compiler warning */
    761 void *
    762 libanastasis_plugin_authorization_post_done (void *cls)
    763 {
    764   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
    765   struct PostContext *ctx = plugin->cls;
    766 
    767   GNUNET_free (ctx->auth_command);
    768   json_decref (ctx->messages);
    769   GNUNET_free (ctx);
    770   GNUNET_free (plugin);
    771   return NULL;
    772 }