LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_auth.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 76.4 % 229 175
Test Date: 2025-12-29 21:26:17 Functions: 100.0 % 12 12

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2014--2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Lesser General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER 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 General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file taler-merchant-httpd_auth.c
      18              :  * @brief client authentication logic
      19              :  * @author Martin Schanzenbach
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <gnunet/gnunet_util_lib.h>
      24              : #include <gnunet/gnunet_db_lib.h>
      25              : #include <taler/taler_json_lib.h>
      26              : #include "taler-merchant-httpd_auth.h"
      27              : #include "taler-merchant-httpd_helper.h"
      28              : 
      29              : /**
      30              :  * Maximum length of a permissions string of a scope
      31              :  */
      32              : #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
      33              : 
      34              : /**
      35              :  * Maximum length of a name of a scope
      36              :  */
      37              : #define TMH_MAX_NAME_LEN 255
      38              : 
      39              : /**
      40              :  * Represents a hard-coded set of default scopes with their
      41              :  * permissions and names
      42              :  */
      43              : struct ScopePermissionMap
      44              : {
      45              :   /**
      46              :    * The scope enum value
      47              :    */
      48              :   enum TMH_AuthScope as;
      49              : 
      50              :   /**
      51              :    * The scope name
      52              :    */
      53              :   char name[TMH_MAX_NAME_LEN];
      54              : 
      55              :   /**
      56              :    * The scope permissions string.
      57              :    * Comma-separated.
      58              :    */
      59              :   char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
      60              : };
      61              : 
      62              : /**
      63              :  * The default scopes array for merchant
      64              :  */
      65              : static struct ScopePermissionMap scope_permissions[] = {
      66              :   /* Deprecated since v19 */
      67              :   {
      68              :     .as = TMH_AS_ALL,
      69              :     .name = "write",
      70              :     .permissions = "*"
      71              :   },
      72              :   /* Full access for SPA */
      73              :   {
      74              :     .as = TMH_AS_ALL,
      75              :     .name = "all",
      76              :     .permissions = "*"
      77              :   },
      78              :   /* Full access for SPA */
      79              :   {
      80              :     .as = TMH_AS_SPA,
      81              :     .name = "spa",
      82              :     .permissions = "*"
      83              :   },
      84              :   /* Read-only access */
      85              :   {
      86              :     .as = TMH_AS_READ_ONLY,
      87              :     .name = "readonly",
      88              :     .permissions = "*-read"
      89              :   },
      90              :   /* Simple order management */
      91              :   {
      92              :     .as = TMH_AS_ORDER_SIMPLE,
      93              :     .name = "order-simple",
      94              :     .permissions = "orders-read,orders-write"
      95              :   },
      96              :   /* Simple order management for PoS, also allows inventory locking */
      97              :   {
      98              :     .as = TMH_AS_ORDER_POS,
      99              :     .name = "order-pos",
     100              :     .permissions = "orders-read,orders-write,inventory-lock"
     101              :   },
     102              :   /* Simple order management, also allows refunding */
     103              :   {
     104              :     .as = TMH_AS_ORDER_MGMT,
     105              :     .name = "order-mgmt",
     106              :     .permissions = "orders-read,orders-write,orders-refund"
     107              :   },
     108              :   /* Full order management, allows inventory locking and refunds */
     109              :   {
     110              :     .as = TMH_AS_ORDER_FULL,
     111              :     .name = "order-full",
     112              :     .permissions = "orders-read,orders-write,inventory-lock,orders-refund"
     113              :   },
     114              :   /* No permissions, dummy scope */
     115              :   {
     116              :     .as = TMH_AS_NONE,
     117              :   }
     118              : };
     119              : 
     120              : 
     121              : /**
     122              :  * Get permissions string for scope.
     123              :  * Also extracts the leftmost bit into the @a refreshable
     124              :  * output parameter.
     125              :  *
     126              :  * @param as the scope to get the permissions string from
     127              :  * @param[out] refreshable true if the token associated with this scope is refreshable.
     128              :  * @return the permissions string, or NULL if no such scope found
     129              :  */
     130              : static const char*
     131          566 : get_scope_permissions (enum TMH_AuthScope as,
     132              :                        bool *refreshable)
     133              : {
     134          566 :   *refreshable = as & TMH_AS_REFRESHABLE;
     135          767 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     136              :   {
     137              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     138          749 :     if ( (as & ~TMH_AS_REFRESHABLE)  ==
     139          749 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     140          548 :       return scope_permissions[i].permissions;
     141              :   }
     142           18 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     143              :               "Failed to find required permissions for scope %d\n",
     144              :               as);
     145           18 :   return NULL;
     146              : }
     147              : 
     148              : 
     149              : /**
     150              :  * Extract the token from authorization header value @a auth.
     151              :  * The @a auth value can be a bearer token or a Basic
     152              :  * authentication header. In both cases, this function
     153              :  * updates @a auth to point to the actual credential,
     154              :  * skipping spaces.
     155              :  *
     156              :  * NOTE: We probably want to replace this function with MHD2
     157              :  * API calls in the future that are more robust.
     158              :  *
     159              :  * @param[in,out] auth pointer to authorization header value,
     160              :  *        will be updated to point to the start of the token
     161              :  *        or set to NULL if header value is invalid
     162              :  * @param[out] is_basic_auth will be set to true if the
     163              :  *        authorization header uses basic authentication,
     164              :  *        otherwise to false
     165              :  */
     166              : static void
     167           71 : extract_auth (const char **auth,
     168              :               bool *is_basic_auth)
     169              : {
     170           71 :   const char *bearer = "Bearer ";
     171           71 :   const char *basic = "Basic ";
     172           71 :   const char *tok = *auth;
     173           71 :   size_t offset = 0;
     174           71 :   bool is_bearer = false;
     175              : 
     176           71 :   *is_basic_auth = false;
     177           71 :   if (0 == strncmp (tok,
     178              :                     bearer,
     179              :                     strlen (bearer)))
     180              :   {
     181           57 :     offset = strlen (bearer);
     182           57 :     is_bearer = true;
     183              :   }
     184           14 :   else if (0 == strncmp (tok,
     185              :                          basic,
     186              :                          strlen (basic)))
     187              :   {
     188           14 :     offset = strlen (basic);
     189           14 :     *is_basic_auth = true;
     190              :   }
     191              :   else
     192              :   {
     193            0 :     *auth = NULL;
     194            0 :     return;
     195              :   }
     196           71 :   tok += offset;
     197           71 :   while (' ' == *tok)
     198            0 :     tok++;
     199           71 :   if ( (is_bearer) &&
     200           57 :        (0 != strncasecmp (tok,
     201              :                           RFC_8959_PREFIX,
     202              :                           strlen (RFC_8959_PREFIX))) )
     203              :   {
     204            0 :     *auth = NULL;
     205            0 :     return;
     206              :   }
     207           71 :   *auth = tok;
     208              : }
     209              : 
     210              : 
     211              : /**
     212              :  * Check if @a userpass grants access to @a instance.
     213              :  *
     214              :  * @param userpass base64 encoded "$USERNAME:$PASSWORD" value
     215              :  *        from HTTP Basic "Authentication" header
     216              :  * @param instance the access controlled instance
     217              :  */
     218              : static enum GNUNET_GenericReturnValue
     219           14 : check_auth_instance (const char *userpass,
     220              :                      struct TMH_MerchantInstance *instance)
     221              : {
     222              :   char *tmp;
     223              :   char *colon;
     224              :   const char *instance_name;
     225              :   const char *password;
     226           14 :   const char *target_instance = "admin";
     227              :   enum GNUNET_GenericReturnValue ret;
     228              : 
     229              :   /* implicitly a zeroed out hash means no authentication */
     230           14 :   if (GNUNET_is_zero (&instance->auth.auth_hash))
     231            0 :     return GNUNET_OK;
     232           14 :   if (NULL == userpass)
     233              :   {
     234            0 :     GNUNET_break_op (0);
     235            0 :     return GNUNET_SYSERR;
     236              :   }
     237           14 :   if (0 ==
     238           14 :       GNUNET_STRINGS_base64_decode (userpass,
     239              :                                     strlen (userpass),
     240              :                                     (void**) &tmp))
     241              :   {
     242            0 :     GNUNET_break_op (0);
     243            0 :     return GNUNET_SYSERR;
     244              :   }
     245           14 :   colon = strchr (tmp,
     246              :                   ':');
     247           14 :   if (NULL == colon)
     248              :   {
     249            0 :     GNUNET_break_op (0);
     250            0 :     GNUNET_free (tmp);
     251            0 :     return GNUNET_SYSERR;
     252              :   }
     253           14 :   *colon = '\0';
     254           14 :   instance_name = tmp;
     255           14 :   password = colon + 1;
     256              :   /* instance->settings.id can be NULL if there is no instance yet */
     257           14 :   if (NULL != instance->settings.id)
     258           14 :     target_instance = instance->settings.id;
     259           14 :   if (0 != strcmp (instance_name,
     260              :                    target_instance))
     261              :   {
     262            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     263              :                 "Somebody tried to login to instance %s with username %s (login failed).\n",
     264              :                 target_instance,
     265              :                 instance_name);
     266            0 :     GNUNET_free (tmp);
     267            0 :     return GNUNET_SYSERR;
     268              :   }
     269           14 :   ret = TMH_check_auth (password,
     270              :                         &instance->auth.auth_salt,
     271              :                         &instance->auth.auth_hash);
     272           14 :   GNUNET_free (tmp);
     273           14 :   if (GNUNET_OK != ret)
     274              :   {
     275            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     276              :                 "Password provided does not match credentials for %s\n",
     277              :                 target_instance);
     278              :   }
     279           14 :   return ret;
     280              : }
     281              : 
     282              : 
     283              : void
     284           13 : TMH_compute_auth (const char *token,
     285              :                   struct TALER_MerchantAuthenticationSaltP *salt,
     286              :                   struct TALER_MerchantAuthenticationHashP *hash)
     287              : {
     288           13 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     289              :                               salt,
     290              :                               sizeof (*salt));
     291           13 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     292              :               "Computing initial auth using token with salt %s\n",
     293              :               TALER_B2S (salt));
     294           13 :   TALER_merchant_instance_auth_hash_with_salt (hash,
     295              :                                                salt,
     296              :                                                token);
     297           13 : }
     298              : 
     299              : 
     300              : /**
     301              :  * Function used to process Basic authorization header value.
     302              :  * Sets correct scope in the auth_scope parameter of the
     303              :  * #TMH_HandlerContext.
     304              :  *
     305              :  * @param hc the handler context
     306              :  * @param authn_s the value of the authorization header
     307              :  */
     308              : static void
     309           14 : process_basic_auth (struct TMH_HandlerContext *hc,
     310              :                     const char *authn_s)
     311              : {
     312              :   /* Handle token endpoint slightly differently: Only allow
     313              :    * instance password (Basic auth) to retrieve access token.
     314              :    * We need to handle authorization with Basic auth here first
     315              :    * The only time we need to handle authentication like this is
     316              :    * for the token endpoint!
     317              :    */
     318           14 :   if ( (0 != strncmp (hc->rh->url_prefix,
     319              :                       "/token",
     320           14 :                       strlen ("/token"))) ||
     321           14 :        (0 != strncmp (MHD_HTTP_METHOD_POST,
     322           14 :                       hc->rh->method,
     323           14 :                       strlen (MHD_HTTP_METHOD_POST))) ||
     324           14 :        (NULL == hc->instance))
     325              :   {
     326            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     327              :                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
     328              :                 hc->rh->url_prefix);
     329            0 :     hc->auth_scope = TMH_AS_NONE;
     330            0 :     return;
     331              :   }
     332           14 :   if (GNUNET_OK ==
     333           14 :       check_auth_instance (authn_s,
     334              :                            hc->instance))
     335              :   {
     336           13 :     hc->auth_scope = TMH_AS_ALL;
     337              :   }
     338              :   else
     339              :   {
     340            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     341              :                 "Basic authentication failed!\n");
     342            1 :     hc->auth_scope = TMH_AS_NONE;
     343              :   }
     344              : }
     345              : 
     346              : 
     347              : /**
     348              :  * Function used to process Bearer authorization header value.
     349              :  * Sets correct scope in the auth_scope parameter of the
     350              :  * #TMH_HandlerContext..
     351              :  *
     352              :  * @param hc the handler context
     353              :  * @param authn_s the value of the authorization header
     354              :  * @return TALER_EC_NONE on success.
     355              :  */
     356              : static enum TALER_ErrorCode
     357          513 : process_bearer_auth (struct TMH_HandlerContext *hc,
     358              :                      const char *authn_s)
     359              : {
     360          513 :   if (NULL == hc->instance)
     361              :   {
     362            2 :     hc->auth_scope = TMH_AS_NONE;
     363            2 :     return TALER_EC_NONE;
     364              :   }
     365          511 :   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
     366              :   {
     367              :     /* hash zero means no authentication for instance */
     368          468 :     hc->auth_scope = TMH_AS_ALL;
     369          468 :     return TALER_EC_NONE;
     370              :   }
     371              :   {
     372              :     enum TALER_ErrorCode ec;
     373              : 
     374           43 :     ec = TMH_check_token (authn_s,
     375           43 :                           hc->instance->settings.id,
     376              :                           &hc->auth_scope);
     377           43 :     if (TALER_EC_NONE != ec)
     378              :     {
     379              :       char *dec;
     380              :       size_t dec_len;
     381              :       const char *token;
     382              : 
     383              :       /* NOTE: Deprecated, remove sometime after v1.1 */
     384            9 :       if (0 != strncasecmp (authn_s,
     385              :                             RFC_8959_PREFIX,
     386              :                             strlen (RFC_8959_PREFIX)))
     387              :       {
     388            0 :         GNUNET_break_op (0);
     389            0 :         hc->auth_scope = TMH_AS_NONE;
     390            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     391              :                     "Authentication token invalid: %d\n",
     392              :                     (int) ec);
     393            9 :         return ec;
     394              :       }
     395            9 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     396              :                   "Trying deprecated secret-token:password API authN\n");
     397            9 :       token = authn_s + strlen (RFC_8959_PREFIX);
     398            9 :       dec_len = GNUNET_STRINGS_urldecode (token,
     399              :                                           strlen (token),
     400              :                                           &dec);
     401           18 :       if ( (0 == dec_len) ||
     402              :            (GNUNET_OK !=
     403            9 :             TMH_check_auth (dec,
     404            9 :                             &hc->instance->auth.auth_salt,
     405            9 :                             &hc->instance->auth.auth_hash)) )
     406              :       {
     407            9 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     408              :                     "Login failed\n");
     409            9 :         hc->auth_scope = TMH_AS_NONE;
     410            9 :         GNUNET_free (dec);
     411            9 :         return TALER_EC_NONE;
     412              :       }
     413            0 :       hc->auth_scope = TMH_AS_ALL;
     414            0 :       GNUNET_free (dec);
     415              :     }
     416              :   }
     417           34 :   return TALER_EC_NONE;
     418              : }
     419              : 
     420              : 
     421              : /**
     422              :  * Checks if @a permission_required is in permissions of
     423              :  * @a scope.
     424              :  *
     425              :  * @param permission_required the permission to check.
     426              :  * @param scope the scope to check.
     427              :  * @return true if @a permission_required is in the permissions set of @a scope.
     428              :  */
     429              : static bool
     430          536 : permission_in_scope (const char *permission_required,
     431              :                      enum TMH_AuthScope scope)
     432              : {
     433              :   char *permissions;
     434              :   const char *perms_tmp;
     435          536 :   bool is_read_perm = false;
     436          536 :   bool is_write_perm = false;
     437              :   bool refreshable;
     438              :   const char *last_dash;
     439              : 
     440          536 :   perms_tmp = get_scope_permissions (scope,
     441              :                                      &refreshable);
     442          536 :   if (NULL == perms_tmp)
     443              :   {
     444           18 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     445              :                 "Permission check failed: scope %d not understood\n",
     446              :                 (int) scope);
     447           18 :     return false;
     448              :   }
     449          518 :   last_dash = strrchr (permission_required,
     450              :                        '-');
     451          518 :   if (NULL != last_dash)
     452              :   {
     453          517 :     is_write_perm = (0 == strcmp (last_dash,
     454              :                                   "-write"));
     455          517 :     is_read_perm = (0 == strcmp (last_dash,
     456              :                                  "-read"));
     457              :   }
     458              : 
     459          518 :   if (0 == strcmp ("token-refresh",
     460              :                    permission_required))
     461              :   {
     462           16 :     if (! refreshable)
     463              :     {
     464            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     465              :                   "Permission check failed: token not refreshable\n");
     466              :     }
     467           16 :     return refreshable;
     468              :   }
     469          502 :   permissions = GNUNET_strdup (perms_tmp);
     470              :   {
     471          502 :     const char *perm = strtok (permissions,
     472              :                                ",");
     473              : 
     474          502 :     if (NULL == perm)
     475              :     {
     476            0 :       GNUNET_free (permissions);
     477            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     478              :                   "Permission check failed: empty permission set\n");
     479            0 :       return false;
     480              :     }
     481          503 :     while (NULL != perm)
     482              :     {
     483          502 :       if (0 == strcmp ("*",
     484              :                        perm))
     485              :       {
     486          500 :         GNUNET_free (permissions);
     487          500 :         return true;
     488              :       }
     489            2 :       if ( (0 == strcmp ("*-write",
     490            0 :                          perm)) &&
     491              :            (is_write_perm) )
     492              :       {
     493            0 :         GNUNET_free (permissions);
     494            0 :         return true;
     495              :       }
     496            2 :       if ( (0 == strcmp ("*-read",
     497            2 :                          perm)) &&
     498              :            (is_read_perm) )
     499              :       {
     500            1 :         GNUNET_free (permissions);
     501            1 :         return true;
     502              :       }
     503            1 :       if (0 == strcmp (permission_required,
     504              :                        perm))
     505              :       {
     506            0 :         GNUNET_free (permissions);
     507            0 :         return true;
     508              :       }
     509            1 :       perm = strtok (NULL,
     510              :                      ",");
     511              :     }
     512              :   }
     513            1 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     514              :               "Permission check failed: %s not found in %s\n",
     515              :               permission_required,
     516              :               permissions);
     517            1 :   GNUNET_free (permissions);
     518            1 :   return false;
     519              : }
     520              : 
     521              : 
     522              : bool
     523           15 : TMH_scope_is_subset (enum TMH_AuthScope as,
     524              :                      enum TMH_AuthScope candidate)
     525              : {
     526              :   const char *as_perms;
     527              :   const char *candidate_perms;
     528              :   char *permissions;
     529              :   bool as_refreshable;
     530              :   bool cand_refreshable;
     531              : 
     532           15 :   as_perms = get_scope_permissions (as,
     533              :                                     &as_refreshable);
     534           15 :   candidate_perms = get_scope_permissions (candidate,
     535              :                                            &cand_refreshable);
     536           15 :   if (! as_refreshable && cand_refreshable)
     537            0 :     return false;
     538           15 :   if ( (NULL == as_perms) &&
     539              :        (NULL != candidate_perms) )
     540            0 :     return false;
     541           15 :   if ( (NULL == candidate_perms) ||
     542           15 :        (0 == strcmp ("*",
     543              :                      as_perms)))
     544           14 :     return true;
     545            1 :   permissions = GNUNET_strdup (candidate_perms);
     546              :   {
     547              :     const char *perm;
     548              : 
     549            1 :     perm = strtok (permissions,
     550              :                    ",");
     551            1 :     if (NULL == perm)
     552              :     {
     553            0 :       GNUNET_free (permissions);
     554            0 :       return true;
     555              :     }
     556            1 :     while (NULL != perm)
     557              :     {
     558            1 :       if (! permission_in_scope (perm,
     559              :                                  as))
     560              :       {
     561            1 :         GNUNET_free (permissions);
     562            1 :         return false;
     563              :       }
     564            0 :       perm = strtok (NULL,
     565              :                      ",");
     566              :     }
     567              :   }
     568            0 :   GNUNET_free (permissions);
     569            0 :   return true;
     570              : }
     571              : 
     572              : 
     573              : enum TMH_AuthScope
     574           15 : TMH_get_scope_by_name (const char *name)
     575              : {
     576           36 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     577              :   {
     578           36 :     if (0 == strcasecmp (scope_permissions[i].name,
     579              :                          name))
     580           15 :       return scope_permissions[i].as;
     581              :   }
     582            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     583              :               "Name `%s' does not match any scope we understand\n",
     584              :               name);
     585            0 :   return TMH_AS_NONE;
     586              : }
     587              : 
     588              : 
     589              : const char*
     590            2 : TMH_get_name_by_scope (enum TMH_AuthScope scope,
     591              :                        bool *refreshable)
     592              : {
     593            2 :   *refreshable = scope & TMH_AS_REFRESHABLE;
     594            8 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     595              :   {
     596              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     597            8 :     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
     598            8 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     599            2 :       return scope_permissions[i].name;
     600              :   }
     601            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     602              :               "Scope #%d does not match any scope we understand\n",
     603              :               (int) scope);
     604            0 :   return NULL;
     605              : }
     606              : 
     607              : 
     608              : enum GNUNET_GenericReturnValue
     609           23 : TMH_check_auth (const char *password,
     610              :                 struct TALER_MerchantAuthenticationSaltP *salt,
     611              :                 struct TALER_MerchantAuthenticationHashP *hash)
     612              : {
     613              :   struct TALER_MerchantAuthenticationHashP val;
     614              : 
     615           23 :   if (GNUNET_is_zero (hash))
     616            0 :     return GNUNET_OK;
     617           23 :   if (NULL == password)
     618              :   {
     619            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     620              :                 "Denying access: empty password provided\n");
     621            0 :     return GNUNET_SYSERR;
     622              :   }
     623           23 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     624              :               "Checking against token with salt %s\n",
     625              :               TALER_B2S (salt));
     626           23 :   TALER_merchant_instance_auth_hash_with_salt (&val,
     627              :                                                salt,
     628              :                                                password);
     629           23 :   if (0 !=
     630           23 :       GNUNET_memcmp (&val,
     631              :                      hash))
     632              :   {
     633           10 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     634              :                 "Access denied: password does not match\n");
     635           10 :     return GNUNET_SYSERR;
     636              :   }
     637           13 :   return GNUNET_OK;
     638              : }
     639              : 
     640              : 
     641              : /**
     642              :  * Check if the client has provided the necessary credentials
     643              :  * to access the selected endpoint of the selected instance.
     644              :  *
     645              :  * @param[in,out] hc handler context
     646              :  * @return #GNUNET_OK on success,
     647              :  *         #GNUNET_NO if an error was queued (return #MHD_YES)
     648              :  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
     649              :  */
     650              : enum GNUNET_GenericReturnValue
     651          548 : TMH_perform_access_control (struct TMH_HandlerContext *hc)
     652              : {
     653              :   const char *auth;
     654          548 :   bool is_basic_auth = false;
     655          548 :   bool auth_malformed = false;
     656              : 
     657          548 :   auth = MHD_lookup_connection_value (hc->connection,
     658              :                                       MHD_HEADER_KIND,
     659              :                                       MHD_HTTP_HEADER_AUTHORIZATION);
     660              : 
     661          548 :   if (NULL != auth)
     662              :   {
     663           71 :     extract_auth (&auth,
     664              :                   &is_basic_auth);
     665           71 :     if (NULL == auth)
     666            0 :       auth_malformed = true;
     667           71 :     hc->auth_token = auth;
     668              :   }
     669              : 
     670              :   /* If we have zero configured instances (not even ones that have been
     671              :      purged) or explicitly disabled authentication, THEN we accept anything
     672              :      (no access control), as we then also have no data to protect. */
     673          548 :   if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
     674          527 :       (GNUNET_YES == TMH_auth_disabled))
     675              :   {
     676           21 :     hc->auth_scope = TMH_AS_ALL;
     677              :   }
     678          527 :   else if (is_basic_auth)
     679              :   {
     680           14 :     process_basic_auth (hc,
     681              :                         auth);
     682              :   }
     683              :   else   /* Check bearer token */
     684              :   {
     685              :     enum TALER_ErrorCode ec;
     686              : 
     687          513 :     ec = process_bearer_auth (hc,
     688              :                               auth);
     689          513 :     if (TALER_EC_NONE != ec)
     690              :     {
     691            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     692              :                   "Bearer authentication failed: %d\n",
     693              :                   (int) ec);
     694              :       return (MHD_YES ==
     695            0 :               TALER_MHD_reply_with_ec (hc->connection,
     696              :                                        ec,
     697              :                                        NULL))
     698              :           ? GNUNET_NO
     699            0 :           : GNUNET_SYSERR;
     700              :     }
     701              :   }
     702              :   /* We grant access if:
     703              :      - Endpoint does not require permissions
     704              :      - Authorization scope of bearer token contains permissions
     705              :        required by endpoint.
     706              :    */
     707          548 :   if ( (NULL != hc->rh->permission) &&
     708          535 :        (! permission_in_scope (hc->rh->permission,
     709              :                                hc->auth_scope)))
     710              :   {
     711           19 :     if (auth_malformed &&
     712            0 :         (TMH_AS_NONE == hc->auth_scope) )
     713              :     {
     714            0 :       GNUNET_break_op (0);
     715              :       return (MHD_YES ==
     716            0 :               TALER_MHD_reply_with_error (
     717              :                 hc->connection,
     718              :                 MHD_HTTP_UNAUTHORIZED,
     719              :                 TALER_EC_GENERIC_PARAMETER_MALFORMED,
     720              :                 "'" RFC_8959_PREFIX
     721              :                 "' prefix or 'Bearer' missing in 'Authorization' header"))
     722              :           ? GNUNET_NO
     723            0 :           : GNUNET_SYSERR;
     724              :     }
     725           19 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     726              :                 "Credentials provided are %d which are insufficient for access to `%s'\n",
     727              :                 (int) hc->auth_scope,
     728              :                 hc->rh->permission);
     729              :     return (MHD_YES ==
     730           19 :             TALER_MHD_reply_with_error (
     731              :               hc->connection,
     732              :               MHD_HTTP_UNAUTHORIZED,
     733              :               TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
     734              :               "Check credentials in 'Authorization' header"))
     735              :         ? GNUNET_NO
     736           19 :         : GNUNET_SYSERR;
     737              :   }
     738          529 :   return GNUNET_OK;
     739              : }
        

Generated by: LCOV version 2.0-1