LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-challenge-ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 2.1 % 193 4
Test Date: 2025-10-30 20:50:50 Functions: 10.0 % 10 1

            Line data    Source code
       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 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              : 
      30              : 
      31              : /**
      32              :  * How many attempts do we allow per solution at most? Note that
      33              :  * this is just for the API, the value must also match the
      34              :  * database logic in create_mfa_challenge.
      35              :  */
      36              : #define MAX_SOLUTIONS 3
      37              : 
      38              : 
      39              : /**
      40              :  * How long is an OTP code valid?
      41              :  */
      42              : #define OTP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
      43              : 
      44              : 
      45              : /**
      46              :  * Internal state for MFA processing.
      47              :  */
      48              : struct MfaState
      49              : {
      50              : 
      51              :   /**
      52              :    * Kept in a DLL.
      53              :    */
      54              :   struct MfaState *next;
      55              : 
      56              :   /**
      57              :    * Kept in a DLL.
      58              :    */
      59              :   struct MfaState *prev;
      60              : 
      61              :   /**
      62              :    * HTTP request we are handling.
      63              :    */
      64              :   struct TMH_HandlerContext *hc;
      65              : 
      66              :   /**
      67              :    * Challenge code.
      68              :    */
      69              :   char *code;
      70              : 
      71              :   /**
      72              :    * When does @e code expire?
      73              :    */
      74              :   struct GNUNET_TIME_Absolute expiration_date;
      75              : 
      76              :   /**
      77              :    * When may we transmit a new code?
      78              :    */
      79              :   struct GNUNET_TIME_Absolute retransmission_date;
      80              : 
      81              :   /**
      82              :    * Handle to the helper process.
      83              :    */
      84              :   struct GNUNET_OS_Process *child;
      85              : 
      86              :   /**
      87              :    * Handle to wait for @e child
      88              :    */
      89              :   struct GNUNET_ChildWaitHandle *cwh;
      90              : 
      91              :   /**
      92              :    * Address where to send the challenge.
      93              :    */
      94              :   char *required_address;
      95              : 
      96              :   /**
      97              :    * Message to send.
      98              :    */
      99              :   char *msg;
     100              : 
     101              :   /**
     102              :    * Offset of transmission in msg.
     103              :    */
     104              :   size_t msg_off;
     105              : 
     106              :   /**
     107              :    * ID of our challenge.
     108              :    */
     109              :   uint64_t challenge_id;
     110              : 
     111              :   /**
     112              :    * Salted hash over the request body.
     113              :    */
     114              :   struct TALER_MERCHANT_MFA_BodyHash h_body;
     115              : 
     116              :   /**
     117              :    * Channel to use for the challenge.
     118              :    */
     119              :   enum TALER_MERCHANT_MFA_Channel channel;
     120              : 
     121              :   enum
     122              :   {
     123              :     MFA_PHASE_PARSE = 0,
     124              :     MFA_PHASE_LOOKUP,
     125              :     MFA_PHASE_SENDING,
     126              :     MFA_PHASE_SUSPENDING,
     127              :     MFA_PHASE_SENT,
     128              :     MFA_PHASE_RETURN_YES,
     129              :     MFA_PHASE_RETURN_NO,
     130              : 
     131              :   } phase;
     132              : 
     133              : 
     134              :   /**
     135              :    * #GNUNET_NO if the @e connection was not suspended,
     136              :    * #GNUNET_YES if the @e connection was suspended,
     137              :    * #GNUNET_SYSERR if @e connection was resumed to as
     138              :    * part of #THM_mfa_done during shutdown.
     139              :    */
     140              :   enum GNUNET_GenericReturnValue suspended;
     141              : 
     142              :   /**
     143              :    * Type of critical operation being authorized.
     144              :    */
     145              :   enum TALER_MERCHANT_MFA_CriticalOperation op;
     146              : 
     147              :   /**
     148              :    * Set to true if sending worked.
     149              :    */
     150              :   bool send_ok;
     151              : };
     152              : 
     153              : 
     154              : /**
     155              :  * Kept in a DLL.
     156              :  */
     157              : static struct MfaState *mfa_head;
     158              : 
     159              : /**
     160              :  * Kept in a DLL.
     161              :  */
     162              : static struct MfaState *mfa_tail;
     163              : 
     164              : 
     165              : /**
     166              :  * Clean up @a mfa process.
     167              :  *
     168              :  * @param[in] cls the `struct MfaState` to clean up
     169              :  */
     170              : static void
     171            0 : mfa_context_cleanup (void *cls)
     172              : {
     173            0 :   struct MfaState *mfa = cls;
     174              : 
     175            0 :   GNUNET_CONTAINER_DLL_remove (mfa_head,
     176              :                                mfa_tail,
     177              :                                mfa);
     178            0 :   if (NULL != mfa->cwh)
     179              :   {
     180            0 :     GNUNET_wait_child_cancel (mfa->cwh);
     181            0 :     mfa->cwh = NULL;
     182              :   }
     183            0 :   if (NULL != mfa->child)
     184              :   {
     185            0 :     (void) GNUNET_OS_process_kill (mfa->child,
     186              :                                    SIGKILL);
     187            0 :     GNUNET_break (GNUNET_OK ==
     188              :                   GNUNET_OS_process_wait (mfa->child));
     189            0 :     mfa->child = NULL;
     190              :   }
     191            0 :   GNUNET_free (mfa->required_address);
     192            0 :   GNUNET_free (mfa->msg);
     193            0 :   GNUNET_free (mfa->code);
     194            0 :   GNUNET_free (mfa);
     195            0 : }
     196              : 
     197              : 
     198              : void
     199           15 : TMH_challenge_done ()
     200              : {
     201           15 :   for (struct MfaState *mfa = mfa_head;
     202           15 :        NULL != mfa;
     203            0 :        mfa = mfa->next)
     204              :   {
     205            0 :     if (GNUNET_YES == mfa->suspended)
     206              :     {
     207            0 :       mfa->suspended = GNUNET_SYSERR;
     208            0 :       MHD_resume_connection (mfa->hc->connection);
     209              :     }
     210              :   }
     211           15 : }
     212              : 
     213              : 
     214              : /**
     215              :  * Send the given @a response for the @a mfa request.
     216              :  *
     217              :  * @param[in,out] mfa process to generate an error response for
     218              :  * @param response_code response code to use
     219              :  * @param[in] response response data to send back
     220              :  */
     221              : static void
     222            0 : respond_to_challenge_with_response (struct MfaState *mfa,
     223              :                                     unsigned int response_code,
     224              :                                     struct MHD_Response *response)
     225              : {
     226              :   MHD_RESULT res;
     227              : 
     228            0 :   res = MHD_queue_response (mfa->hc->connection,
     229              :                             response_code,
     230              :                             response);
     231            0 :   MHD_destroy_response (response);
     232            0 :   mfa->phase = (MHD_NO == res)
     233              :     ? MFA_PHASE_RETURN_NO
     234            0 :     : MFA_PHASE_RETURN_YES;
     235            0 : }
     236              : 
     237              : 
     238              : /**
     239              :  * Generate an error for @a mfa.
     240              :  *
     241              :  * @param[in,out] mfa process to generate an error response for
     242              :  * @param http_status HTTP status of the response
     243              :  * @param ec Taler error code to return
     244              :  * @param hint hint to return, can be NULL
     245              :  */
     246              : static void
     247            0 : respond_with_error (struct MfaState *mfa,
     248              :                     unsigned int http_status,
     249              :                     enum TALER_ErrorCode ec,
     250              :                     const char *hint)
     251              : {
     252            0 :   respond_to_challenge_with_response (
     253              :     mfa,
     254              :     http_status,
     255              :     TALER_MHD_make_error (ec,
     256              :                           hint));
     257            0 : }
     258              : 
     259              : 
     260              : /**
     261              :  * Challenge code transmission complete. Continue based on the result.
     262              :  *
     263              :  * @param[in,out] mfa process to send the challenge for
     264              :  */
     265              : static void
     266            0 : phase_sent (struct MfaState *mfa)
     267              : {
     268              :   enum GNUNET_DB_QueryStatus qs;
     269              : 
     270            0 :   if (! mfa->send_ok)
     271              :   {
     272            0 :     respond_with_error (mfa,
     273              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     274              :                         TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
     275              :                         "process exited with error");
     276            0 :     return;
     277              :   }
     278            0 :   qs = TMH_db->update_mfa_challenge (TMH_db->cls,
     279              :                                      mfa->challenge_id,
     280            0 :                                      mfa->code,
     281              :                                      MAX_SOLUTIONS,
     282              :                                      mfa->expiration_date,
     283              :                                      mfa->retransmission_date);
     284            0 :   switch (qs)
     285              :   {
     286            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     287            0 :     GNUNET_break (0);
     288            0 :     respond_with_error (mfa,
     289              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     290              :                         TALER_EC_GENERIC_DB_COMMIT_FAILED,
     291              :                         "update_mfa_challenge");
     292            0 :     return;
     293            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     294            0 :     GNUNET_break (0);
     295            0 :     respond_with_error (mfa,
     296              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     297              :                         TALER_EC_GENERIC_DB_SOFT_FAILURE,
     298              :                         "update_mfa_challenge");
     299            0 :     return;
     300            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     301            0 :     GNUNET_break (0);
     302            0 :     respond_with_error (mfa,
     303              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     304              :                         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     305              :                         "no results on INSERT, but success?");
     306            0 :     return;
     307            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     308            0 :     break;
     309              :   }
     310              :   {
     311              :     struct MHD_Response *response;
     312              : 
     313              :     response =
     314            0 :       TALER_MHD_make_json_steal (
     315            0 :         GNUNET_JSON_PACK (
     316              :           GNUNET_JSON_pack_timestamp (
     317              :             "solve_expiration",
     318              :             GNUNET_TIME_absolute_to_timestamp (
     319              :               mfa->expiration_date)),
     320              :           GNUNET_JSON_pack_timestamp (
     321              :             "earliest_retransmission",
     322              :             GNUNET_TIME_absolute_to_timestamp (
     323              :               mfa->retransmission_date))));
     324            0 :     respond_to_challenge_with_response (
     325              :       mfa,
     326              :       MHD_HTTP_OK,
     327              :       response);
     328              :   }
     329              : }
     330              : 
     331              : 
     332              : /**
     333              :  * Function called when our SMS helper has terminated.
     334              :  *
     335              :  * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
     336              :  * @param type type of the process
     337              :  * @param exit_code status code of the process
     338              :  */
     339              : static void
     340            0 : transmission_done_cb (void *cls,
     341              :                       enum GNUNET_OS_ProcessStatusType type,
     342              :                       long unsigned int exit_code)
     343              : {
     344            0 :   struct MfaState *mfa = cls;
     345              : 
     346            0 :   mfa->cwh = NULL;
     347            0 :   if (NULL != mfa->child)
     348              :   {
     349            0 :     GNUNET_OS_process_destroy (mfa->child);
     350            0 :     mfa->child = NULL;
     351              :   }
     352            0 :   mfa->send_ok = ( (GNUNET_OS_PROCESS_EXITED == type) &&
     353            0 :                    (0 == exit_code) );
     354            0 :   if (! mfa->send_ok)
     355            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     356              :                 "MFA helper failed with status %d/%u\n",
     357              :                 (int) type,
     358              :                 (unsigned int) exit_code);
     359            0 :   mfa->phase = MFA_PHASE_SENT;
     360            0 :   GNUNET_assert (GNUNET_YES == mfa->suspended);
     361            0 :   mfa->suspended = GNUNET_NO;
     362            0 :   MHD_resume_connection (mfa->hc->connection);
     363            0 :   TALER_MHD_daemon_trigger ();
     364            0 : }
     365              : 
     366              : 
     367              : /**
     368              :  * Setup challenge code for @a mfa and send it to the
     369              :  * @a required_address; on success.
     370              :  *
     371              :  * @param[in,out] mfa process to send the challenge for
     372              :  * @param required_address where to send the challenge
     373              :  */
     374              : static void
     375            0 : phase_send_challenge (struct MfaState *mfa)
     376              : {
     377              :   const char *prog;
     378              : 
     379            0 :   switch (mfa->channel)
     380              :   {
     381            0 :   case TALER_MERCHANT_MFA_CHANNEL_NONE:
     382            0 :     GNUNET_assert (0);
     383              :     break;
     384            0 :   case TALER_MERCHANT_MFA_CHANNEL_SMS:
     385              :     mfa->expiration_date
     386            0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     387              :     mfa->retransmission_date
     388            0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     389            0 :     GNUNET_asprintf (&mfa->code,
     390              :                      "%llu",
     391              :                      (unsigned long long)
     392            0 :                      GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
     393              :                                                100000000));
     394            0 :     prog = TMH_helper_sms;
     395            0 :     break;
     396            0 :   case TALER_MERCHANT_MFA_CHANNEL_EMAIL:
     397              :     mfa->expiration_date
     398            0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     399              :     mfa->retransmission_date
     400            0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS);
     401            0 :     GNUNET_asprintf (&mfa->code,
     402              :                      "%llu",
     403              :                      (unsigned long long)
     404            0 :                      GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
     405              :                                                100000000));
     406            0 :     prog = TMH_helper_email;
     407            0 :     break;
     408            0 :   case TALER_MERCHANT_MFA_CHANNEL_TOTP:
     409              :     mfa->expiration_date
     410            0 :       = GNUNET_TIME_relative_to_absolute (OTP_TIMEOUT);
     411              :     mfa->retransmission_date
     412            0 :       = GNUNET_TIME_relative_to_absolute (OTP_TIMEOUT);
     413            0 :     respond_with_error (mfa,
     414              :                         MHD_HTTP_NOT_IMPLEMENTED,
     415              :                         TALER_EC_GENERIC_FEATURE_NOT_IMPLEMENTED,
     416              :                         "#10327");
     417            0 :     return;
     418              :   }
     419            0 :   if (NULL == prog)
     420              :   {
     421            0 :     respond_with_error (
     422              :       mfa,
     423              :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     424              :       TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     425              :       TALER_MERCHANT_MFA_channel_to_string (mfa->channel));
     426            0 :     return;
     427              :   }
     428              :   {
     429              :     /* Start child process and feed pipe */
     430              :     struct GNUNET_DISK_PipeHandle *p;
     431              :     struct GNUNET_DISK_FileHandle *pipe_stdin;
     432              : 
     433            0 :     p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
     434            0 :     if (NULL == p)
     435              :     {
     436            0 :       respond_with_error (mfa,
     437              :                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     438              :                           TALER_EC_GENERIC_ALLOCATION_FAILURE,
     439              :                           "pipe");
     440            0 :       return;
     441              :     }
     442            0 :     mfa->child = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
     443              :                                           p,
     444              :                                           NULL,
     445              :                                           NULL,
     446              :                                           prog,
     447              :                                           prog,
     448              :                                           mfa->required_address,
     449              :                                           NULL);
     450            0 :     if (NULL == mfa->child)
     451              :     {
     452            0 :       GNUNET_break (GNUNET_OK ==
     453              :                     GNUNET_DISK_pipe_close (p));
     454            0 :       respond_with_error (mfa,
     455              :                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     456              :                           TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
     457              :                           "exec");
     458            0 :       return;
     459              :     }
     460              : 
     461            0 :     pipe_stdin = GNUNET_DISK_pipe_detach_end (p,
     462              :                                               GNUNET_DISK_PIPE_END_WRITE);
     463            0 :     GNUNET_assert (NULL != pipe_stdin);
     464            0 :     GNUNET_break (GNUNET_OK ==
     465              :                   GNUNET_DISK_pipe_close (p));
     466            0 :     GNUNET_asprintf (&mfa->msg,
     467              :                      "%s\nTaler-Merchant:\n%s",
     468              :                      mfa->code,
     469              :                      TALER_MERCHANT_MFA_co2s (mfa->op));
     470              :     {
     471            0 :       const char *off = mfa->msg;
     472            0 :       size_t left = strlen (off);
     473              : 
     474            0 :       while (0 != left)
     475              :       {
     476              :         ssize_t ret;
     477              : 
     478            0 :         ret = GNUNET_DISK_file_write (pipe_stdin,
     479              :                                       off,
     480              :                                       left);
     481            0 :         if (ret <= 0)
     482              :         {
     483            0 :           respond_with_error (mfa,
     484              :                               MHD_HTTP_INTERNAL_SERVER_ERROR,
     485              :                               TALER_EC_MERCHANT_TAN_MFA_HELPER_EXEC_FAILED,
     486              :                               "write");
     487            0 :           return;
     488              :         }
     489            0 :         mfa->msg_off += ret;
     490            0 :         off += ret;
     491            0 :         left -= ret;
     492              :       }
     493            0 :       GNUNET_DISK_file_close (pipe_stdin);
     494              :     }
     495              :   }
     496            0 :   mfa->phase = MFA_PHASE_SUSPENDING;
     497              : }
     498              : 
     499              : 
     500              : /**
     501              :  * Lookup challenge in DB.
     502              :  *
     503              :  * @param[in,out] mfa process to parse data for
     504              :  */
     505              : static void
     506            0 : phase_lookup (struct MfaState *mfa)
     507              : {
     508              :   enum GNUNET_DB_QueryStatus qs;
     509              :   uint32_t retry_counter;
     510              :   struct GNUNET_TIME_Absolute confirmation_date;
     511              :   struct GNUNET_TIME_Absolute retransmission_date;
     512              :   struct TALER_MERCHANT_MFA_BodySalt salt;
     513              : 
     514            0 :   qs = TMH_db->lookup_mfa_challenge (TMH_db->cls,
     515              :                                      mfa->challenge_id,
     516            0 :                                      &mfa->h_body,
     517              :                                      &salt,
     518              :                                      &mfa->required_address,
     519              :                                      &mfa->op,
     520              :                                      &confirmation_date,
     521              :                                      &retransmission_date,
     522              :                                      &retry_counter,
     523              :                                      &mfa->channel);
     524            0 :   switch (qs)
     525              :   {
     526            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     527            0 :     GNUNET_break (0);
     528            0 :     respond_with_error (mfa,
     529              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     530              :                         TALER_EC_GENERIC_DB_COMMIT_FAILED,
     531              :                         "lookup_mfa_challenge");
     532            0 :     return;
     533            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     534            0 :     GNUNET_break (0);
     535            0 :     respond_with_error (mfa,
     536              :                         MHD_HTTP_INTERNAL_SERVER_ERROR,
     537              :                         TALER_EC_GENERIC_DB_SOFT_FAILURE,
     538              :                         "lookup_mfa_challenge");
     539            0 :     return;
     540            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     541            0 :     GNUNET_break (0);
     542            0 :     respond_with_error (mfa,
     543              :                         MHD_HTTP_NOT_FOUND,
     544              :                         TALER_EC_MERCHANT_TAN_CHALLENGE_UNKNOWN,
     545            0 :                         mfa->hc->infix);
     546            0 :     return;
     547            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     548            0 :     break;
     549              :   }
     550            0 :   if (! GNUNET_TIME_absolute_is_future (confirmation_date))
     551              :   {
     552              :     /* was already solved */
     553            0 :     respond_with_error (mfa,
     554              :                         MHD_HTTP_GONE,
     555              :                         TALER_EC_MERCHANT_TAN_CHALLENGE_SOLVED,
     556              :                         NULL);
     557            0 :     return;
     558              :   }
     559            0 :   if (GNUNET_TIME_absolute_is_future (retransmission_date))
     560              :   {
     561              :     /* too early to try again */
     562            0 :     respond_with_error (mfa,
     563              :                         MHD_HTTP_TOO_MANY_REQUESTS,
     564              :                         TALER_EC_MERCHANT_TAN_TOO_EARLY,
     565              :                         GNUNET_TIME_absolute2s (retransmission_date));
     566            0 :     return;
     567              :   }
     568            0 :   mfa->phase++;
     569              : }
     570              : 
     571              : 
     572              : /**
     573              :  * Parse challenge request.
     574              :  *
     575              :  * @param[in,out] mfa process to parse data for
     576              :  */
     577              : static void
     578            0 : phase_parse (struct MfaState *mfa)
     579              : {
     580            0 :   struct TMH_HandlerContext *hc = mfa->hc;
     581              :   enum GNUNET_GenericReturnValue ret;
     582              : 
     583            0 :   ret = TMH_mfa_parse_challenge_id (hc,
     584            0 :                                     hc->infix,
     585              :                                     &mfa->challenge_id,
     586              :                                     &mfa->h_body);
     587            0 :   if (GNUNET_OK != ret)
     588              :   {
     589            0 :     mfa->phase = (GNUNET_NO == ret)
     590              :       ? MFA_PHASE_RETURN_YES
     591            0 :       : MFA_PHASE_RETURN_NO;
     592            0 :     return;
     593              :   }
     594            0 :   mfa->phase++;
     595              : }
     596              : 
     597              : 
     598              : MHD_RESULT
     599            0 : TMH_post_challenge_ID (const struct TMH_RequestHandler *rh,
     600              :                        struct MHD_Connection *connection,
     601              :                        struct TMH_HandlerContext *hc)
     602              : {
     603            0 :   struct MfaState *mfa = hc->ctx;
     604              : 
     605            0 :   if (NULL == mfa)
     606              :   {
     607            0 :     mfa = GNUNET_new (struct MfaState);
     608            0 :     mfa->hc = hc;
     609            0 :     hc->ctx = mfa;
     610            0 :     hc->cc = &mfa_context_cleanup;
     611            0 :     GNUNET_CONTAINER_DLL_insert (mfa_head,
     612              :                                  mfa_tail,
     613              :                                  mfa);
     614              :   }
     615              : 
     616              :   while (1)
     617              :   {
     618            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     619              :                 "Processing /challenge in phase %d\n",
     620              :                 (int) mfa->phase);
     621            0 :     switch (mfa->phase)
     622              :     {
     623            0 :     case MFA_PHASE_PARSE:
     624            0 :       phase_parse (mfa);
     625            0 :       break;
     626            0 :     case MFA_PHASE_LOOKUP:
     627            0 :       phase_lookup (mfa);
     628            0 :       break;
     629            0 :     case MFA_PHASE_SENDING:
     630            0 :       phase_send_challenge (mfa);
     631            0 :       break;
     632            0 :     case MFA_PHASE_SUSPENDING:
     633            0 :       mfa->cwh = GNUNET_wait_child (mfa->child,
     634              :                                     &transmission_done_cb,
     635              :                                     mfa);
     636            0 :       if (NULL == mfa->cwh)
     637              :       {
     638            0 :         respond_with_error (mfa,
     639              :                             MHD_HTTP_INTERNAL_SERVER_ERROR,
     640              :                             TALER_EC_GENERIC_ALLOCATION_FAILURE,
     641              :                             "GNUNET_wait_child");
     642            0 :         continue;
     643              :       }
     644            0 :       mfa->suspended = GNUNET_YES;
     645            0 :       MHD_suspend_connection (hc->connection);
     646            0 :       return MHD_YES;
     647            0 :     case MFA_PHASE_SENT:
     648            0 :       phase_sent (mfa);
     649            0 :       break;
     650            0 :     case MFA_PHASE_RETURN_YES:
     651            0 :       return MHD_YES;
     652            0 :     case MFA_PHASE_RETURN_NO:
     653            0 :       GNUNET_break (0);
     654            0 :       return MHD_NO;
     655              :     }
     656              :   }
     657              : }
        

Generated by: LCOV version 2.0-1