gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-anastasis] branch master updated: implement post plugin


From: gnunet
Subject: [taler-anastasis] branch master updated: implement post plugin
Date: Thu, 11 Mar 2021 11:21:05 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository anastasis.

The following commit(s) were added to refs/heads/master by this push:
     new 4676c7f  implement post plugin
4676c7f is described below

commit 4676c7fba291a7cf46afc74d00e91256e284344c
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Thu Mar 11 11:21:03 2021 +0100

    implement post plugin
---
 contrib/gana                                       |   2 +-
 src/authorization/Makefile.am                      |  12 +
 .../anastasis_authorization_plugin_post.c          | 553 +++++++++++++++++++++
 src/util/child_management.c                        |   2 -
 4 files changed, 566 insertions(+), 3 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 4c5ae13..880352a 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 4c5ae13bf6c7e3cb070e9d56065cfdfc053f0b3a
+Subproject commit 880352a419beee4864f863182dbc695c859ee3f7
diff --git a/src/authorization/Makefile.am b/src/authorization/Makefile.am
index b1af53d..bce6db3 100644
--- a/src/authorization/Makefile.am
+++ b/src/authorization/Makefile.am
@@ -23,6 +23,7 @@ libanastasisauthorization_la_LDFLAGS = \
 plugin_LTLIBRARIES = \
   libanastasis_plugin_authorization_email.la \
   libanastasis_plugin_authorization_file.la \
+  libanastasis_plugin_authorization_post.la \
   libanastasis_plugin_authorization_sms.la
 libanastasis_plugin_authorization_file_la_SOURCES = \
   anastasis_authorization_plugin_file.c
@@ -46,6 +47,17 @@ libanastasis_plugin_authorization_email_la_LDFLAGS = \
   -lgnunetutil \
   $(XLIB)
 
+libanastasis_plugin_authorization_post_la_SOURCES = \
+  anastasis_authorization_plugin_post.c
+libanastasis_plugin_authorization_post_la_LIBADD = \
+  $(LTLIBINTL)
+libanastasis_plugin_authorization_post_la_LDFLAGS = \
+  $(ANASTASIS_PLUGIN_LDFLAGS) \
+  -ljansson \
+  -ltalerutil \
+  -lgnunetutil \
+  $(XLIB)
+
 libanastasis_plugin_authorization_sms_la_SOURCES = \
   anastasis_authorization_plugin_sms.c
 libanastasis_plugin_authorization_sms_la_LIBADD = \
diff --git a/src/authorization/anastasis_authorization_plugin_post.c 
b/src/authorization/anastasis_authorization_plugin_post.c
new file mode 100644
index 0000000..f926402
--- /dev/null
+++ b/src/authorization/anastasis_authorization_plugin_post.c
@@ -0,0 +1,553 @@
+/*
+  This file is part of Anastasis
+  Copyright (C) 2021 Taler Systems SA
+
+  Anastasis is free software; you can redistribute it and/or modify it under 
the
+  terms of the GNU Lesser General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  Anastasis; see the file COPYING.GPL.  If not, see 
<http://www.gnu.org/licenses/>
+*/
+/**
+ * @file include/anastasis_authorization_plugin_post.c
+ * @brief authorization plugin post based
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "anastasis_authorization_plugin.h"
+#include <taler/taler_mhd_lib.h>
+#include <jansson.h>
+#include "anastasis_util_lib.h"
+
+
+/**
+ * Saves the State of a authorization plugin.
+ */
+struct PostContext
+{
+
+  /**
+   * Command which is executed to run the plugin (some bash script or a
+   * command line argument)
+   */
+  char *auth_command;
+
+};
+
+
+/**
+ * Saves the state of a authorization process
+ */
+struct ANASTASIS_AUTHORIZATION_State
+{
+  /**
+   * Public key of the challenge which is authorised
+   */
+  struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid;
+
+  /**
+   * Code which is sent to the user.
+   */
+  uint64_t code;
+
+  /**
+   * Our plugin context.
+   */
+  struct PostContext *ctx;
+
+  /**
+   * Function to call when we made progress.
+   */
+  GNUNET_SCHEDULER_TaskCallback trigger;
+
+  /**
+   * Closure for @e trigger.
+   */
+  void *trigger_cls;
+
+  /**
+   * holds the truth information
+   */
+  json_t *post;
+
+  /**
+   * Handle to the helper process.
+   */
+  struct GNUNET_OS_Process *child;
+
+  /**
+   * Handle to wait for @e child
+   */
+  struct ANASTASIS_ChildWaitHandle *cwh;
+
+  /**
+   * Our client connection, set if suspended.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Message to send.
+   */
+  char *msg;
+
+  /**
+   * Offset of transmission in msg.
+   */
+  size_t msg_off;
+
+  /**
+   * Exit code from helper.
+   */
+  long unsigned int exit_code;
+
+  /**
+   * How did the helper die?
+   */
+  enum GNUNET_OS_ProcessStatusType pst;
+
+
+};
+
+
+/**
+ * Validate @a data is a well-formed input into the challenge method,
+ * i.e. @a data is a well-formed phone number for sending an SMS, or
+ * a well-formed e-mail address for sending an e-mail. Not expected to
+ * check that the phone number or e-mail account actually exists.
+ *
+ * To be possibly used before issuing a 402 payment required to the client.
+ *
+ * @param cls closure
+ * @param connection HTTP client request (for queuing response)
+ * @param truth_mime mime type of @e data
+ * @param data input to validate (i.e. is it a valid phone number, etc.)
+ * @param data_length number of bytes in @a data
+ * @return #GNUNET_OK if @a data is valid,
+ *         #GNUNET_NO if @a data is invalid and a reply was successfully 
queued on @a connection
+ *         #GNUNET_SYSERR if @a data invalid but we failed to queue a reply on 
@a connection
+ */
+static enum GNUNET_GenericReturnValue
+post_validate (void *cls,
+               struct MHD_Connection *connection,
+               const char *mime_type,
+               const char *data,
+               size_t data_length)
+{
+  struct PostContext *ctx = cls;
+  json_t *j;
+  json_error_t error;
+  const char *name;
+  const char *street;
+  const char *city;
+  const char *zip;
+  const char *country;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("full_name",
+                             &name),
+    GNUNET_JSON_spec_string ("street",
+                             &street),
+    GNUNET_JSON_spec_string ("city",
+                             &city),
+    GNUNET_JSON_spec_string ("postcode",
+                             &zip),
+    GNUNET_JSON_spec_string ("country",
+                             &country),
+    GNUNET_JSON_spec_end ()
+  };
+
+  (void) ctx;
+  j = json_loadb (data,
+                  data_length,
+                  JSON_REJECT_DUPLICATES,
+                  &error);
+  if (NULL == j)
+  {
+    if (MHD_NO ==
+        TALER_MHD_reply_with_error (connection,
+                                    MHD_HTTP_EXPECTATION_FAILED,
+                                    TALER_EC_ANASTASIS_POST_INVALID,
+                                    "JSON malformed"))
+      return GNUNET_SYSERR;
+    return GNUNET_NO;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break (0);
+    json_decref (j);
+    if (MHD_NO ==
+        TALER_MHD_reply_with_error (connection,
+                                    MHD_HTTP_EXPECTATION_FAILED,
+                                    TALER_EC_ANASTASIS_POST_INVALID,
+                                    "JSON lacked required address 
information"))
+      return GNUNET_SYSERR;
+    return GNUNET_NO;
+  }
+  json_decref (j);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin issuing authentication challenge to user based on @a data.
+ * I.e. start to send mail.
+ *
+ * @param cls closure
+ * @param trigger function to call when we made progress
+ * @param trigger_cls closure for @a trigger
+ * @param truth_uuid Identifier of the challenge, to be (if possible) included 
in the
+ *             interaction with the user
+ * @param code secret code that the user has to provide back to satisfy the 
challenge in
+ *             the main anastasis protocol
+ * @param data input to validate (i.e. is it a valid phone number, etc.)
+ * @return state to track progress on the authorization operation, NULL on 
failure
+ */
+static struct ANASTASIS_AUTHORIZATION_State *
+post_start (void *cls,
+            GNUNET_SCHEDULER_TaskCallback trigger,
+            void *trigger_cls,
+            const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid,
+            uint64_t code,
+            const void *data,
+            size_t data_length)
+{
+  struct PostContext *ctx = cls;
+  struct ANASTASIS_AUTHORIZATION_State *as;
+  json_error_t error;
+
+  as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
+  as->trigger = trigger;
+  as->trigger_cls = trigger_cls;
+  as->ctx = ctx;
+  as->truth_uuid = *truth_uuid;
+  as->code = code;
+  as->post = json_loadb (data,
+                         data_length,
+                         JSON_REJECT_DUPLICATES,
+                         &error);
+  if (NULL == as->post)
+  {
+    GNUNET_break (0);
+    GNUNET_free (as);
+    return NULL;
+  }
+  return as;
+}
+
+
+/**
+ * Function called when our Post helper has terminated.
+ *
+ * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
+ * @param type type of the process
+ * @param exit_code status code of the process
+ */
+static void
+post_done_cb (void *cls,
+              enum GNUNET_OS_ProcessStatusType type,
+              long unsigned int exit_code)
+{
+  struct ANASTASIS_AUTHORIZATION_State *as = cls;
+
+  as->child = NULL;
+  as->cwh = NULL;
+  as->pst = type;
+  as->exit_code = exit_code;
+  MHD_resume_connection (as->connection);
+  as->trigger (as->trigger_cls);
+}
+
+
+/**
+ * Begin issuing authentication challenge to user based on @a data.
+ * I.e. start to send SMS or e-mail or launch video identification.
+ *
+ * @param as authorization state
+ * @param connection HTTP client request (for queuing response, such as 
redirection to video portal)
+ * @return state of the request
+ */
+static enum ANASTASIS_AUTHORIZATION_Result
+post_process (struct ANASTASIS_AUTHORIZATION_State *as,
+              struct MHD_Connection *connection)
+{
+  MHD_RESULT mres;
+  const char *name;
+  const char *street;
+  const char *city;
+  const char *zip;
+  const char *country;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("full_name",
+                             &name),
+    GNUNET_JSON_spec_string ("street",
+                             &street),
+    GNUNET_JSON_spec_string ("city",
+                             &city),
+    GNUNET_JSON_spec_string ("postcode",
+                             &zip),
+    GNUNET_JSON_spec_string ("country",
+                             &country),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (as->post,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break (0);
+    mres = TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_ANASTASIS_POST_INVALID,
+                                       "address information incomplete");
+    if (MHD_YES != mres)
+      return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+    return ANASTASIS_AUTHORIZATION_RES_FAILED;
+  }
+  if (NULL == as->msg)
+  {
+    /* First time, start child process and feed pipe */
+    struct GNUNET_DISK_PipeHandle *p;
+    struct GNUNET_DISK_FileHandle *pipe_stdin;
+
+    p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
+    if (NULL == p)
+    {
+      mres = TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
+                                         "pipe");
+      if (MHD_YES != mres)
+        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+      return ANASTASIS_AUTHORIZATION_RES_FAILED;
+    }
+    as->child = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
+                                         p,
+                                         NULL,
+                                         NULL,
+                                         as->ctx->auth_command,
+                                         as->ctx->auth_command,
+                                         name,
+                                         street,
+                                         city,
+                                         zip,
+                                         country,
+                                         NULL);
+    if (NULL == as->child)
+    {
+      GNUNET_DISK_pipe_close (p);
+      mres = TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
+                                         "exec");
+      if (MHD_YES != mres)
+        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+      return ANASTASIS_AUTHORIZATION_RES_FAILED;
+    }
+    pipe_stdin = GNUNET_DISK_pipe_detach_end (p,
+                                              GNUNET_DISK_PIPE_END_WRITE);
+    GNUNET_assert (NULL != pipe_stdin);
+    GNUNET_DISK_pipe_close (p);
+    {
+      char *tpk;
+
+      tpk = GNUNET_STRINGS_data_to_string_alloc (
+        &as->truth_uuid,
+        sizeof (as->truth_uuid));
+      GNUNET_asprintf (&as->msg,
+                       "Dear Customer\n\n"
+                       "The Anastasis recovery code you need to\n"
+                       "recover your data is #%llu.\n"
+                       "This is for challenge %s.\n\n"
+                       "Best regards\n\n"
+                       "Your Anastasis provider",
+                       (unsigned long long) as->code,
+                       tpk);
+      GNUNET_free (tpk);
+    }
+
+    {
+      const char *off = as->msg;
+      size_t left = strlen (off);
+
+      while (0 != left)
+      {
+        ssize_t ret;
+
+        if (0 == left)
+          break;
+        ret = GNUNET_DISK_file_write (pipe_stdin,
+                                      off,
+                                      left);
+        if (ret <= 0)
+        {
+          mres = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             
TALER_EC_ANASTASIS_POST_HELPER_EXEC_FAILED,
+                                             "write");
+          if (MHD_YES != mres)
+            return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+          return ANASTASIS_AUTHORIZATION_RES_FAILED;
+        }
+        as->msg_off += ret;
+        off += ret;
+        left -= ret;
+      }
+      GNUNET_DISK_file_close (pipe_stdin);
+    }
+    as->cwh = ANASTASIS_wait_child (as->child,
+                                    &post_done_cb,
+                                    as);
+    as->connection = connection;
+    MHD_suspend_connection (connection);
+    return ANASTASIS_AUTHORIZATION_RES_SUSPENDED;
+  }
+  if (NULL != as->cwh)
+  {
+    /* Spurious call, why are we here? */
+    GNUNET_break (0);
+    MHD_suspend_connection (connection);
+    return ANASTASIS_AUTHORIZATION_RES_SUSPENDED;
+  }
+  if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
+       (0 != as->exit_code) )
+  {
+    char es[32];
+
+    GNUNET_snprintf (es,
+                     sizeof (es),
+                     "%u/%d",
+                     (unsigned int) as->exit_code,
+                     as->pst);
+    mres = TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_ANASTASIS_POST_HELPER_COMMAND_FAILED,
+                                       es);
+    if (MHD_YES != mres)
+      return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+    return ANASTASIS_AUTHORIZATION_RES_FAILED;
+  }
+
+  /* Build HTTP response */
+  {
+    struct MHD_Response *resp;
+    size_t reply_len;
+    char *reply;
+
+    /* FIXME: i18n based on language preferences of the client */
+    /* FIXME: Reply in JSON if requested by client */
+    reply_len = GNUNET_asprintf (&reply,
+                                 "Recovery message zip code %s",
+                                 zip);
+    resp = MHD_create_response_from_buffer (reply_len,
+                                            reply,
+                                            MHD_RESPMEM_MUST_COPY);
+    GNUNET_free (reply);
+    TALER_MHD_add_global_headers (resp);
+    mres = MHD_queue_response (connection,
+                               MHD_HTTP_FORBIDDEN,
+                               resp);
+    MHD_destroy_response (resp);
+  }
+
+  if (MHD_YES != mres)
+    return ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED;
+  return ANASTASIS_AUTHORIZATION_RES_SUCCESS;
+}
+
+
+/**
+ * Free internal state associated with @a as.
+ *
+ * @param as state to clean up
+ */
+static void
+post_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
+{
+  if (NULL != as->cwh)
+  {
+    ANASTASIS_wait_child_cancel (as->cwh);
+    as->cwh = NULL;
+  }
+  if (NULL != as->child)
+  {
+    (void) GNUNET_OS_process_kill (as->child,
+                                   SIGKILL);
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_OS_process_wait (as->child));
+    as->child = NULL;
+  }
+  GNUNET_free (as->msg);
+  json_decref (as->post);
+  GNUNET_free (as);
+}
+
+
+/**
+ * Initialize post based authorization plugin
+ *
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct ANASTASIS_AuthorizationPlugin`
+ */
+void *
+libanastasis_plugin_authorization_post_init (void *cls)
+{
+  struct ANASTASIS_AuthorizationPlugin *plugin;
+  struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct PostContext *ctx;
+
+  ctx = GNUNET_new (struct PostContext);
+  plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
+  plugin->code_validity_period = GNUNET_TIME_UNIT_MONTHS;
+  plugin->code_rotation_period = GNUNET_TIME_UNIT_WEEKS;
+  plugin->code_retransmission_frequency
+    = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
+                                     2);
+  plugin->cls = ctx;
+  plugin->validate = &post_validate;
+  plugin->start = &post_start;
+  plugin->process = &post_process;
+  plugin->cleanup = &post_cleanup;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "authorization-post",
+                                             "COMMAND",
+                                             &ctx->auth_command))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "authorization-post",
+                               "COMMAND");
+    GNUNET_free (ctx);
+    GNUNET_free (plugin);
+    return NULL;
+  }
+  return plugin;
+}
+
+
+/**
+ * Unload authorization plugin
+ *
+ * @param cls a `struct ANASTASIS_AuthorizationPlugin`
+ * @return NULL (always)
+ */
+void *
+libanastasis_plugin_authorization_post_done (void *cls)
+{
+  struct ANASTASIS_AuthorizationPlugin *plugin = cls;
+  struct PostContext *ctx = plugin->cls;
+
+  GNUNET_free (ctx->auth_command);
+  GNUNET_free (ctx);
+  GNUNET_free (plugin);
+  return NULL;
+}
diff --git a/src/util/child_management.c b/src/util/child_management.c
index 156daf8..267b4db 100644
--- a/src/util/child_management.c
+++ b/src/util/child_management.c
@@ -19,10 +19,8 @@
  * @author Christian Grothoff
  * @author Dominik Meister
  */
-
 #include "platform.h"
 #include "anastasis_util_lib.h"
-#include <gnunet/gnunet_util_lib.h>
 
 
 /**

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]