merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_post-challenge-ID.c (20354B)


      1 /*
      2   This file is part of TALER
      3   (C) 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file src/backend/taler-merchant-httpd_post-challenge-ID.c
     22  * @brief endpoint to trigger sending MFA challenge
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "taler-merchant-httpd.h"
     27 #include "taler-merchant-httpd_mfa.h"
     28 #include "taler-merchant-httpd_post-challenge-ID.h"
     29 #include "merchant-database/lookup_mfa_challenge.h"
     30 #include "merchant-database/update_mfa_challenge.h"
     31 
     32 
     33 /**
     34  * How many attempts do we allow per solution at most? Note that
     35  * this is just for the API, the value must also match the
     36  * database logic in create_mfa_challenge.
     37  */
     38 #define MAX_SOLUTIONS 3
     39 
     40 
     41 /**
     42  * How long is an OTP code valid?
     43  */
     44 #define OTP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
     45 
     46 
     47 /**
     48  * Internal state for MFA processing.
     49  */
     50 struct MfaState
     51 {
     52 
     53   /**
     54    * Kept in a DLL.
     55    */
     56   struct MfaState *next;
     57 
     58   /**
     59    * Kept in a DLL.
     60    */
     61   struct MfaState *prev;
     62 
     63   /**
     64    * HTTP request we are handling.
     65    */
     66   struct TMH_HandlerContext *hc;
     67 
     68   /**
     69    * Challenge code.
     70    */
     71   char *code;
     72 
     73   /**
     74    * When does @e code expire?
     75    */
     76   struct GNUNET_TIME_Absolute expiration_date;
     77 
     78   /**
     79    * When may we transmit a new code?
     80    */
     81   struct GNUNET_TIME_Absolute retransmission_date;
     82 
     83   /**
     84    * Handle to the helper process.
     85    */
     86   struct GNUNET_Process *child;
     87 
     88   /**
     89    * Handle to wait for @e child
     90    */
     91   struct GNUNET_ChildWaitHandle *cwh;
     92 
     93   /**
     94    * Address where to send the challenge.
     95    */
     96   char *required_address;
     97 
     98   /**
     99    * Message to send.
    100    */
    101   char *msg;
    102 
    103   /**
    104    * Instance the challenge is for.
    105    */
    106   char *instance_id;
    107 
    108   /**
    109    * Offset of transmission in msg.
    110    */
    111   size_t msg_off;
    112 
    113   /**
    114    * ID of our challenge.
    115    */
    116   uint64_t challenge_id;
    117 
    118   /**
    119    * Salted hash over the request body.
    120    */
    121   struct TALER_MERCHANT_MFA_BodyHash h_body;
    122 
    123   /**
    124    * Channel to use for the challenge.
    125    */
    126   enum TALER_MERCHANT_MFA_Channel channel;
    127 
    128   enum
    129   {
    130     MFA_PHASE_PARSE = 0,
    131     MFA_PHASE_LOOKUP,
    132     MFA_PHASE_SENDING,
    133     MFA_PHASE_SUSPENDING,
    134     MFA_PHASE_SENT,
    135     MFA_PHASE_RETURN_YES,
    136     MFA_PHASE_RETURN_NO,
    137 
    138   } phase;
    139 
    140 
    141   /**
    142    * #GNUNET_NO if the @e connection was not suspended,
    143    * #GNUNET_YES if the @e connection was suspended,
    144    * #GNUNET_SYSERR if @e connection was resumed to as
    145    * part of #THM_mfa_done during shutdown.
    146    */
    147   enum GNUNET_GenericReturnValue suspended;
    148 
    149   /**
    150    * Type of critical operation being authorized.
    151    */
    152   enum TALER_MERCHANT_MFA_CriticalOperation op;
    153 
    154   /**
    155    * Set to true if sending worked.
    156    */
    157   bool send_ok;
    158 };
    159 
    160 
    161 /**
    162  * Kept in a DLL.
    163  */
    164 static struct MfaState *mfa_head;
    165 
    166 /**
    167  * Kept in a DLL.
    168  */
    169 static struct MfaState *mfa_tail;
    170 
    171 
    172 /**
    173  * Clean up @a mfa process.
    174  *
    175  * @param[in] cls the `struct MfaState` to clean up
    176  */
    177 static void
    178 mfa_context_cleanup (void *cls)
    179 {
    180   struct MfaState *mfa = cls;
    181 
    182   GNUNET_CONTAINER_DLL_remove (mfa_head,
    183                                mfa_tail,
    184                                mfa);
    185   if (NULL != mfa->cwh)
    186   {
    187     GNUNET_wait_child_cancel (mfa->cwh);
    188     mfa->cwh = NULL;
    189   }
    190   if (NULL != mfa->child)
    191   {
    192     GNUNET_break (GNUNET_OK ==
    193                   GNUNET_process_kill (mfa->child,
    194                                        SIGKILL));
    195     GNUNET_break (GNUNET_OK ==
    196                   GNUNET_process_wait (mfa->child,
    197                                        true,
    198                                        NULL,
    199                                        NULL));
    200     GNUNET_process_destroy (mfa->child);
    201     mfa->child = NULL;
    202   }
    203   GNUNET_free (mfa->required_address);
    204   GNUNET_free (mfa->msg);
    205   GNUNET_free (mfa->instance_id);
    206   GNUNET_free (mfa->code);
    207   GNUNET_free (mfa);
    208 }
    209 
    210 
    211 void
    212 TMH_challenge_done ()
    213 {
    214   for (struct MfaState *mfa = mfa_head;
    215        NULL != mfa;
    216        mfa = mfa->next)
    217   {
    218     if (GNUNET_YES == mfa->suspended)
    219     {
    220       /* Make sure transmission_done_cb() cannot fire after
    221          we resumed the connection here. */
    222       if (NULL != mfa->cwh)
    223       {
    224         GNUNET_wait_child_cancel (mfa->cwh);
    225         mfa->cwh = NULL;
    226       }
    227       mfa->suspended = GNUNET_SYSERR;
    228       MHD_resume_connection (mfa->hc->connection);
    229     }
    230   }
    231 }
    232 
    233 
    234 /**
    235  * Send the given @a response for the @a mfa request.
    236  *
    237  * @param[in,out] mfa process to generate an error response for
    238  * @param response_code response code to use
    239  * @param[in] response response data to send back
    240  */
    241 static void
    242 respond_to_challenge_with_response (struct MfaState *mfa,
    243                                     unsigned int response_code,
    244                                     struct MHD_Response *response)
    245 {
    246   enum MHD_Result res;
    247 
    248   res = MHD_queue_response (mfa->hc->connection,
    249                             response_code,
    250                             response);
    251   MHD_destroy_response (response);
    252   mfa->phase = (MHD_NO == res)
    253     ? MFA_PHASE_RETURN_NO
    254     : MFA_PHASE_RETURN_YES;
    255 }
    256 
    257 
    258 /**
    259  * Generate an error for @a mfa.
    260  *
    261  * @param[in,out] mfa process to generate an error response for
    262  * @param http_status HTTP status of the response
    263  * @param ec Taler error code to return
    264  * @param hint hint to return, can be NULL
    265  */
    266 static void
    267 respond_with_error (struct MfaState *mfa,
    268                     unsigned int http_status,
    269                     enum TALER_ErrorCode ec,
    270                     const char *hint)
    271 {
    272   respond_to_challenge_with_response (
    273     mfa,
    274     http_status,
    275     TALER_MHD_make_error (ec,
    276                           hint));
    277 }
    278 
    279 
    280 /**
    281  * Challenge code transmission complete. Continue based on the result.
    282  *
    283  * @param[in,out] mfa process to send the challenge for
    284  */
    285 static void
    286 phase_sent (struct MfaState *mfa)
    287 {
    288   enum GNUNET_DB_QueryStatus qs;
    289 
    290   if (! mfa->send_ok)
    291   {
    292     respond_with_error (mfa,
    293                         MHD_HTTP_BAD_GATEWAY,
    294                         TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
    295                         "process exited with error");
    296     return;
    297   }
    298   qs = TALER_MERCHANTDB_update_mfa_challenge (TMH_db,
    299                                               mfa->challenge_id,
    300                                               mfa->code,
    301                                               MAX_SOLUTIONS,
    302                                               mfa->expiration_date,
    303                                               mfa->retransmission_date);
    304   switch (qs)
    305   {
    306   case GNUNET_DB_STATUS_HARD_ERROR:
    307     GNUNET_break (0);
    308     respond_with_error (mfa,
    309                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    310                         TALER_EC_GENERIC_DB_COMMIT_FAILED,
    311                         "update_mfa_challenge");
    312     return;
    313   case GNUNET_DB_STATUS_SOFT_ERROR:
    314     GNUNET_break (0);
    315     respond_with_error (mfa,
    316                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    317                         TALER_EC_GENERIC_DB_SOFT_FAILURE,
    318                         "update_mfa_challenge");
    319     return;
    320   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    321     GNUNET_break (0);
    322     respond_with_error (mfa,
    323                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    324                         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    325                         "no results on INSERT, but success?");
    326     return;
    327   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    328     break;
    329   }
    330   {
    331     struct MHD_Response *response;
    332 
    333     response =
    334       TALER_MHD_make_json_steal (
    335         GNUNET_JSON_PACK (
    336           GNUNET_JSON_pack_timestamp (
    337             "solve_expiration",
    338             GNUNET_TIME_absolute_to_timestamp (
    339               mfa->expiration_date)),
    340           GNUNET_JSON_pack_timestamp (
    341             "earliest_retransmission",
    342             GNUNET_TIME_absolute_to_timestamp (
    343               mfa->retransmission_date))));
    344     respond_to_challenge_with_response (
    345       mfa,
    346       MHD_HTTP_OK,
    347       response);
    348   }
    349 }
    350 
    351 
    352 /**
    353  * Function called when our SMS helper has terminated.
    354  *
    355  * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
    356  * @param type type of the process
    357  * @param exit_code status code of the process
    358  */
    359 static void
    360 transmission_done_cb (void *cls,
    361                       enum GNUNET_OS_ProcessStatusType type,
    362                       long unsigned int exit_code)
    363 {
    364   struct MfaState *mfa = cls;
    365 
    366   mfa->cwh = NULL;
    367   if (NULL != mfa->child)
    368   {
    369     GNUNET_process_destroy (mfa->child);
    370     mfa->child = NULL;
    371   }
    372   mfa->send_ok = ( (GNUNET_OS_PROCESS_EXITED == type) &&
    373                    (0 == exit_code) );
    374   if (! mfa->send_ok)
    375     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    376                 "MFA helper failed with status %d/%u\n",
    377                 (int) type,
    378                 (unsigned int) exit_code);
    379   mfa->phase = MFA_PHASE_SENT;
    380   GNUNET_assert (GNUNET_YES == mfa->suspended);
    381   mfa->suspended = GNUNET_NO;
    382   MHD_resume_connection (mfa->hc->connection);
    383   TALER_MHD_daemon_trigger ();
    384 }
    385 
    386 
    387 /**
    388  * Resolve a binary name via PATH.
    389  *
    390  * Needed because the GNUnet process helpers to not support
    391  * an execp equivalent at present.
    392  *
    393  * @param binary_name name to search for
    394  * @returns resolved path or NULL if not found
    395  */
    396 static char *
    397 resolve_path (const char *binary_name)
    398 {
    399   char *path_env;
    400   char full_path[2048];
    401   char *dir;
    402   char *path_copy;
    403 
    404   if (NULL != strchr (binary_name,
    405                       '/'))
    406   {
    407     /* Already a full path, do not search. */
    408     return GNUNET_strdup (binary_name);
    409   }
    410   path_env = getenv ("PATH");
    411   if (path_env == NULL)
    412     return NULL;
    413   /* Duplicate PATH because strtok modifies the string it parses */
    414   path_copy = GNUNET_strdup (path_env);
    415   dir = strtok (path_copy, ":");
    416   while (dir != NULL)
    417   {
    418     snprintf (full_path,
    419               sizeof(full_path),
    420               "%s/%s",
    421               dir,
    422               binary_name);
    423     if (0 == access (full_path,
    424                      X_OK))
    425     {
    426       GNUNET_free (path_copy);
    427       return GNUNET_strdup (full_path);
    428     }
    429     dir = strtok (NULL, ":");
    430   }
    431   GNUNET_free (path_copy);
    432   return NULL;
    433 }
    434 
    435 
    436 /**
    437  * Setup challenge code for @a mfa and send it to the
    438  * @a required_address; on success.
    439  *
    440  * @param[in,out] mfa process to send the challenge for
    441  */
    442 static void
    443 phase_send_challenge (struct MfaState *mfa)
    444 {
    445   const char *prog = NULL;
    446   char *binary_path = NULL;
    447   unsigned long long challenge_num;
    448   char **cmd_argv = NULL;
    449 
    450   challenge_num = (unsigned long long)
    451                   GNUNET_CRYPTO_random_u64 (1000 * 1000 * 100);
    452   GNUNET_asprintf (&mfa->code,
    453                    "%04llu-%04llu",
    454                    challenge_num / 10000,
    455                    challenge_num % 10000);
    456   switch (mfa->channel)
    457   {
    458   case TALER_MERCHANT_MFA_CHANNEL_NONE:
    459     GNUNET_assert (0);
    460     break;
    461   case TALER_MERCHANT_MFA_CHANNEL_SMS:
    462     mfa->expiration_date
    463       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
    464     mfa->retransmission_date
    465       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
    466     prog = TMH_helper_sms;
    467     break;
    468   case TALER_MERCHANT_MFA_CHANNEL_EMAIL:
    469     mfa->expiration_date
    470       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
    471     mfa->retransmission_date
    472       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
    473     prog = TMH_helper_email;
    474     break;
    475   case TALER_MERCHANT_MFA_CHANNEL_TOTP:
    476     mfa->expiration_date
    477       = GNUNET_TIME_relative_to_absolute (OTP_TIMEOUT);
    478     mfa->retransmission_date
    479       = GNUNET_TIME_relative_to_absolute (OTP_TIMEOUT);
    480     respond_with_error (mfa,
    481                         MHD_HTTP_NOT_IMPLEMENTED,
    482                         TALER_EC_GENERIC_FEATURE_NOT_IMPLEMENTED,
    483                         "#10327");
    484     goto done;
    485   }
    486   if (NULL == prog)
    487   {
    488     respond_with_error (
    489       mfa,
    490       MHD_HTTP_INTERNAL_SERVER_ERROR,
    491       TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    492       TALER_MERCHANT_MFA_channel_to_string (mfa->channel));
    493     goto done;
    494   }
    495   {
    496     /* Start child process and feed pipe */
    497     struct GNUNET_DISK_PipeHandle *p;
    498     struct GNUNET_DISK_FileHandle *pipe_stdin;
    499     const char *extra_args[] = {
    500       mfa->required_address,
    501       NULL,
    502     };
    503 
    504     cmd_argv = TALER_words_split (prog,
    505                                   extra_args);
    506 
    507     GNUNET_assert (NULL != cmd_argv[0]);
    508 
    509     p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
    510     if (NULL == p)
    511     {
    512       respond_with_error (mfa,
    513                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    514                           TALER_EC_GENERIC_ALLOCATION_FAILURE,
    515                           "pipe");
    516       goto done;
    517     }
    518     mfa->child = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
    519     GNUNET_assert (GNUNET_OK ==
    520                    GNUNET_process_set_options (
    521                      mfa->child,
    522                      GNUNET_process_option_inherit_rpipe (p,
    523                                                           STDIN_FILENO)));
    524     binary_path = resolve_path (cmd_argv[0]);
    525     if ( (NULL == binary_path) ||
    526          (GNUNET_OK !=
    527           GNUNET_process_run_command_argv (mfa->child,
    528                                            binary_path,
    529                                            (const char **) cmd_argv)) )
    530     {
    531       GNUNET_process_destroy (mfa->child);
    532       mfa->child = NULL;
    533       GNUNET_break (GNUNET_OK ==
    534                     GNUNET_DISK_pipe_close (p));
    535       respond_with_error (mfa,
    536                           MHD_HTTP_BAD_GATEWAY,
    537                           TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
    538                           "exec");
    539       goto done;
    540     }
    541 
    542     pipe_stdin = GNUNET_DISK_pipe_detach_end (p,
    543                                               GNUNET_DISK_PIPE_END_WRITE);
    544     GNUNET_assert (NULL != pipe_stdin);
    545     GNUNET_break (GNUNET_OK ==
    546                   GNUNET_DISK_pipe_close (p));
    547     GNUNET_asprintf (&mfa->msg,
    548                      "%s is your security code.\n"
    549                      "Do not share your code with anyone.\n\n"
    550                      "Authorizes: %s\n"
    551                      "Login: %s\n\n"
    552                      "Expires: %s (%s).\n",
    553                      mfa->code,
    554                      TALER_MERCHANT_MFA_co2s (mfa->op),
    555                      mfa->instance_id,
    556                      GNUNET_TIME_absolute2s (
    557                        mfa->expiration_date),
    558                      GNUNET_TIME_relative2s (
    559                        GNUNET_TIME_absolute_get_remaining (
    560                          mfa->expiration_date),
    561                        true));
    562     {
    563       const char *off = mfa->msg;
    564       size_t left = strlen (off);
    565 
    566       while (0 != left)
    567       {
    568         ssize_t ret;
    569 
    570         ret = GNUNET_DISK_file_write (pipe_stdin,
    571                                       off,
    572                                       left);
    573         if (ret <= 0)
    574         {
    575           GNUNET_DISK_file_close (pipe_stdin);
    576           respond_with_error (mfa,
    577                               MHD_HTTP_BAD_GATEWAY,
    578                               TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
    579                               "write");
    580           goto done;
    581         }
    582         mfa->msg_off += ret;
    583         off += ret;
    584         left -= ret;
    585       }
    586       GNUNET_DISK_file_close (pipe_stdin);
    587     }
    588   }
    589   mfa->phase = MFA_PHASE_SUSPENDING;
    590 done:
    591   GNUNET_free (binary_path);
    592   TALER_words_destroy (cmd_argv);
    593 }
    594 
    595 
    596 /**
    597  * Lookup challenge in DB.
    598  *
    599  * @param[in,out] mfa process to parse data for
    600  */
    601 static void
    602 phase_lookup (struct MfaState *mfa)
    603 {
    604   enum GNUNET_DB_QueryStatus qs;
    605   uint32_t retry_counter;
    606   struct GNUNET_TIME_Absolute confirmation_date;
    607   struct GNUNET_TIME_Absolute retransmission_date;
    608   struct TALER_MERCHANT_MFA_BodySalt salt;
    609 
    610   qs = TALER_MERCHANTDB_lookup_mfa_challenge (TMH_db,
    611                                               mfa->challenge_id,
    612                                               &mfa->h_body,
    613                                               &salt,
    614                                               &mfa->required_address,
    615                                               &mfa->op,
    616                                               &confirmation_date,
    617                                               &retransmission_date,
    618                                               &retry_counter,
    619                                               &mfa->channel,
    620                                               &mfa->instance_id);
    621   switch (qs)
    622   {
    623   case GNUNET_DB_STATUS_HARD_ERROR:
    624     GNUNET_break (0);
    625     respond_with_error (mfa,
    626                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    627                         TALER_EC_GENERIC_DB_COMMIT_FAILED,
    628                         "lookup_mfa_challenge");
    629     return;
    630   case GNUNET_DB_STATUS_SOFT_ERROR:
    631     GNUNET_break (0);
    632     respond_with_error (mfa,
    633                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    634                         TALER_EC_GENERIC_DB_SOFT_FAILURE,
    635                         "lookup_mfa_challenge");
    636     return;
    637   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    638     GNUNET_break_op (0);
    639     respond_with_error (mfa,
    640                         MHD_HTTP_NOT_FOUND,
    641                         TALER_EC_MERCHANT_TAN_CHALLENGE_UNKNOWN,
    642                         mfa->hc->infix);
    643     return;
    644   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    645     break;
    646   }
    647   if (! GNUNET_TIME_absolute_is_future (confirmation_date))
    648   {
    649     /* was already solved */
    650     respond_with_error (mfa,
    651                         MHD_HTTP_GONE,
    652                         TALER_EC_MERCHANT_TAN_CHALLENGE_SOLVED,
    653                         NULL);
    654     return;
    655   }
    656   if (GNUNET_TIME_absolute_is_future (retransmission_date))
    657   {
    658     /* too early to try again */
    659     respond_with_error (mfa,
    660                         MHD_HTTP_TOO_MANY_REQUESTS,
    661                         TALER_EC_MERCHANT_TAN_TOO_EARLY,
    662                         GNUNET_TIME_absolute2s (retransmission_date));
    663     return;
    664   }
    665   mfa->phase++;
    666 }
    667 
    668 
    669 /**
    670  * Parse challenge request.
    671  *
    672  * @param[in,out] mfa process to parse data for
    673  */
    674 static void
    675 phase_parse (struct MfaState *mfa)
    676 {
    677   struct TMH_HandlerContext *hc = mfa->hc;
    678   enum GNUNET_GenericReturnValue ret;
    679 
    680   ret = TMH_mfa_parse_challenge_id (hc,
    681                                     hc->infix,
    682                                     &mfa->challenge_id,
    683                                     &mfa->h_body);
    684   if (GNUNET_OK != ret)
    685   {
    686     mfa->phase = (GNUNET_NO == ret)
    687       ? MFA_PHASE_RETURN_YES
    688       : MFA_PHASE_RETURN_NO;
    689     return;
    690   }
    691   mfa->phase++;
    692 }
    693 
    694 
    695 enum MHD_Result
    696 TMH_post_challenge_ID (const struct TMH_RequestHandler *rh,
    697                        struct MHD_Connection *connection,
    698                        struct TMH_HandlerContext *hc)
    699 {
    700   struct MfaState *mfa = hc->ctx;
    701 
    702   if (NULL == mfa)
    703   {
    704     mfa = GNUNET_new (struct MfaState);
    705     mfa->hc = hc;
    706     hc->ctx = mfa;
    707     hc->cc = &mfa_context_cleanup;
    708     GNUNET_CONTAINER_DLL_insert (mfa_head,
    709                                  mfa_tail,
    710                                  mfa);
    711   }
    712 
    713   while (1)
    714   {
    715     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    716                 "Processing /challenge in phase %d\n",
    717                 (int) mfa->phase);
    718     switch (mfa->phase)
    719     {
    720     case MFA_PHASE_PARSE:
    721       phase_parse (mfa);
    722       break;
    723     case MFA_PHASE_LOOKUP:
    724       phase_lookup (mfa);
    725       break;
    726     case MFA_PHASE_SENDING:
    727       phase_send_challenge (mfa);
    728       break;
    729     case MFA_PHASE_SUSPENDING:
    730       mfa->cwh = GNUNET_wait_child (mfa->child,
    731                                     &transmission_done_cb,
    732                                     mfa);
    733       if (NULL == mfa->cwh)
    734       {
    735         respond_with_error (mfa,
    736                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    737                             TALER_EC_GENERIC_ALLOCATION_FAILURE,
    738                             "GNUNET_wait_child");
    739         continue;
    740       }
    741       mfa->suspended = GNUNET_YES;
    742       MHD_suspend_connection (hc->connection);
    743       return MHD_YES;
    744     case MFA_PHASE_SENT:
    745       phase_sent (mfa);
    746       break;
    747     case MFA_PHASE_RETURN_YES:
    748       return MHD_YES;
    749     case MFA_PHASE_RETURN_NO:
    750       GNUNET_break (0);
    751       return MHD_NO;
    752     }
    753   }
    754 }