LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_auth.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 78.2 % 234 183
Test Date: 2026-09-11 17:45:24 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 src/backend/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              :   /* Order management for PoS, allows inventory locking and refunds */
      97              :   {
      98              :     .as = TMH_AS_ORDER_POS,
      99              :     .name = "order-pos",
     100              :     .permissions = "orders-read,orders-write,pos-read,products-read,products-lock,orders-refund"
     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,pos-read,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,pos-read,products-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          865 : get_scope_permissions (enum TMH_AuthScope as,
     132              :                        bool *refreshable)
     133              : {
     134          865 :   *refreshable = as & TMH_AS_REFRESHABLE;
     135         1180 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     136              :   {
     137              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     138         1161 :     if ( (as & ~TMH_AS_REFRESHABLE)  ==
     139         1161 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     140          846 :       return scope_permissions[i].permissions;
     141              :   }
     142           19 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     143              :               "Failed to find required permissions for scope %d\n",
     144              :               as);
     145           19 :   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          112 : extract_auth (const char **auth,
     168              :               bool *is_basic_auth)
     169              : {
     170          112 :   const char *bearer = "Bearer ";
     171          112 :   const char *basic = "Basic ";
     172          112 :   const char *tok = *auth;
     173          112 :   size_t offset = 0;
     174          112 :   bool is_bearer = false;
     175              : 
     176          112 :   *is_basic_auth = false;
     177          112 :   if (0 == strncmp (tok,
     178              :                     bearer,
     179              :                     strlen (bearer)))
     180              :   {
     181           92 :     offset = strlen (bearer);
     182           92 :     is_bearer = true;
     183              :   }
     184           20 :   else if (0 == strncmp (tok,
     185              :                          basic,
     186              :                          strlen (basic)))
     187              :   {
     188           20 :     offset = strlen (basic);
     189           20 :     *is_basic_auth = true;
     190              :   }
     191              :   else
     192              :   {
     193            0 :     *auth = NULL;
     194            0 :     return;
     195              :   }
     196          112 :   tok += offset;
     197          112 :   while (' ' == *tok)
     198            0 :     tok++;
     199          112 :   if ( (is_bearer) &&
     200           92 :        (0 != strncasecmp (tok,
     201              :                           RFC_8959_PREFIX,
     202              :                           strlen (RFC_8959_PREFIX))) )
     203              :   {
     204            0 :     *auth = NULL;
     205            0 :     return;
     206              :   }
     207          112 :   *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           20 : check_auth_instance (const char *userpass,
     220              :                      struct TMH_MerchantInstance *instance)
     221              : {
     222              :   char *tmp;
     223              :   char *colon;
     224              :   char *instance_name;
     225              :   const char *password;
     226           20 :   const char *target_instance = "admin";
     227              :   enum GNUNET_GenericReturnValue ret;
     228              : 
     229              :   /* implicitly a zeroed out hash means no authentication */
     230           20 :   if (GNUNET_is_zero (&instance->auth.auth_hash))
     231            0 :     return GNUNET_OK;
     232           20 :   if (NULL == userpass)
     233              :   {
     234            0 :     GNUNET_break_op (0);
     235            0 :     return GNUNET_SYSERR;
     236              :   }
     237           20 :   if (0 ==
     238           20 :       GNUNET_STRINGS_base64_decode (userpass,
     239              :                                     strlen (userpass),
     240              :                                     (void**) &tmp))
     241              :   {
     242              :     /* GNUNET_STRINGS_base64_decode() always allocates its output
     243              :        buffer, even when it returns 0; free it to avoid a leak. */
     244            0 :     GNUNET_break_op (0);
     245            0 :     GNUNET_free (tmp);
     246            0 :     return GNUNET_SYSERR;
     247              :   }
     248           20 :   colon = strchr (tmp,
     249              :                   ':');
     250           20 :   if (NULL == colon)
     251              :   {
     252            0 :     GNUNET_break_op (0);
     253            0 :     GNUNET_free (tmp);
     254            0 :     return GNUNET_SYSERR;
     255              :   }
     256           20 :   *colon = '\0';
     257           20 :   password = colon + 1;
     258              :   /* Instance IDs are stored in canonical (lower-case) form (see
     259              :      GNUNET_STRINGS_utf8_tolower() in the instance creation and lookup
     260              :      paths), so we must fold the username to the same canonical form
     261              :      before comparing; otherwise a mixed-case username would not match
     262              :      the stored id ("myshop") and Basic auth would fail with HTTP 401. */
     263           20 :   instance_name = GNUNET_STRINGS_utf8_tolower (tmp);
     264              :   /* instance->settings.id can be NULL if there is no instance yet */
     265           20 :   if (NULL != instance->settings.id)
     266           20 :     target_instance = instance->settings.id;
     267           20 :   if (0 != strcmp (instance_name,
     268              :                    target_instance))
     269              :   {
     270            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     271              :                 "Somebody tried to login to instance %s with username %s (login failed).\n",
     272              :                 target_instance,
     273              :                 instance_name);
     274            0 :     GNUNET_free (instance_name);
     275            0 :     GNUNET_free (tmp);
     276            0 :     return GNUNET_SYSERR;
     277              :   }
     278           20 :   GNUNET_free (instance_name);
     279           20 :   ret = TMH_check_auth (password,
     280              :                         &instance->auth.auth_salt,
     281              :                         &instance->auth.auth_hash);
     282           20 :   GNUNET_free (tmp);
     283           20 :   if (GNUNET_OK != ret)
     284              :   {
     285            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     286              :                 "Password provided does not match credentials for %s\n",
     287              :                 target_instance);
     288              :   }
     289           20 :   return ret;
     290              : }
     291              : 
     292              : 
     293              : void
     294           20 : TMH_compute_auth (const char *token,
     295              :                   struct TALER_MerchantAuthenticationSaltP *salt,
     296              :                   struct TALER_MerchantAuthenticationHashP *hash)
     297              : {
     298           20 :   GNUNET_CRYPTO_random_block (salt,
     299              :                               sizeof (*salt));
     300           20 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     301              :               "Computing initial auth using token with salt %s\n",
     302              :               TALER_B2S (salt));
     303           20 :   TALER_merchant_instance_auth_hash_with_salt (hash,
     304              :                                                salt,
     305              :                                                token);
     306           20 : }
     307              : 
     308              : 
     309              : /**
     310              :  * Function used to process Basic authorization header value.
     311              :  * Sets correct scope in the auth_scope parameter of the
     312              :  * #TMH_HandlerContext.
     313              :  *
     314              :  * @param hc the handler context
     315              :  * @param authn_s the value of the authorization header
     316              :  */
     317              : static void
     318           20 : process_basic_auth (struct TMH_HandlerContext *hc,
     319              :                     const char *authn_s)
     320              : {
     321              :   /* Handle token endpoint slightly differently: Only allow
     322              :    * instance password (Basic auth) to retrieve access token.
     323              :    * We need to handle authorization with Basic auth here first
     324              :    * The only time we need to handle authentication like this is
     325              :    * for the token endpoint!
     326              :    */
     327           20 :   if ( (0 != strcmp (hc->rh->url_prefix,
     328           20 :                      "/token")) ||
     329           20 :        (NULL == hc->rh->method) ||
     330           20 :        (0 != strcmp (MHD_HTTP_METHOD_POST,
     331           20 :                      hc->rh->method)) ||
     332           20 :        (NULL == hc->instance))
     333              :   {
     334            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     335              :                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
     336              :                 hc->rh->url_prefix);
     337            0 :     hc->auth_scope = TMH_AS_NONE;
     338            0 :     return;
     339              :   }
     340           20 :   if (GNUNET_OK ==
     341           20 :       check_auth_instance (authn_s,
     342              :                            hc->instance))
     343              :   {
     344           19 :     hc->auth_scope = TMH_AS_ALL;
     345              :   }
     346              :   else
     347              :   {
     348            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     349              :                 "Basic authentication failed!\n");
     350            1 :     hc->auth_scope = TMH_AS_NONE;
     351              :   }
     352              : }
     353              : 
     354              : 
     355              : /**
     356              :  * Function used to process Bearer authorization header value.
     357              :  * Sets correct scope in the auth_scope parameter of the
     358              :  * #TMH_HandlerContext..
     359              :  *
     360              :  * @param hc the handler context
     361              :  * @param authn_s the value of the authorization header
     362              :  * @return TALER_EC_NONE on success.
     363              :  */
     364              : static enum TALER_ErrorCode
     365          774 : process_bearer_auth (struct TMH_HandlerContext *hc,
     366              :                      const char *authn_s)
     367              : {
     368          774 :   if (NULL == hc->instance)
     369              :   {
     370            2 :     hc->auth_scope = TMH_AS_NONE;
     371            2 :     return TALER_EC_NONE;
     372              :   }
     373          772 :   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
     374              :   {
     375              :     /* hash zero means no authentication for instance */
     376          705 :     hc->auth_scope = TMH_AS_ALL;
     377          705 :     return TALER_EC_NONE;
     378              :   }
     379              :   {
     380              :     enum TALER_ErrorCode ec;
     381              : 
     382           67 :     ec = TMH_check_token (authn_s,
     383           67 :                           hc->instance->settings.id,
     384              :                           &hc->auth_scope);
     385           67 :     if (TALER_EC_NONE != ec)
     386              :     {
     387              :       char *dec;
     388              :       size_t dec_len;
     389              :       const char *token;
     390              : 
     391              :       /* NOTE: Deprecated, remove sometime after v1.1 */
     392           16 :       if (0 != strncasecmp (authn_s,
     393              :                             RFC_8959_PREFIX,
     394              :                             strlen (RFC_8959_PREFIX)))
     395              :       {
     396            0 :         GNUNET_break_op (0);
     397            0 :         hc->auth_scope = TMH_AS_NONE;
     398            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     399              :                     "Authentication token invalid: %d\n",
     400              :                     (int) ec);
     401           10 :         return ec;
     402              :       }
     403           16 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     404              :                   "Trying deprecated secret-token:password API authN\n");
     405           16 :       token = authn_s + strlen (RFC_8959_PREFIX);
     406           16 :       dec_len = GNUNET_STRINGS_urldecode (token,
     407              :                                           strlen (token),
     408              :                                           &dec);
     409           32 :       if ( (0 == dec_len) ||
     410              :            (GNUNET_OK !=
     411           16 :             TMH_check_auth (dec,
     412           16 :                             &hc->instance->auth.auth_salt,
     413           16 :                             &hc->instance->auth.auth_hash)) )
     414              :       {
     415           10 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     416              :                     "Login failed\n");
     417           10 :         hc->auth_scope = TMH_AS_NONE;
     418           10 :         GNUNET_free (dec);
     419           10 :         return TALER_EC_NONE;
     420              :       }
     421            6 :       hc->auth_scope = TMH_AS_ALL;
     422            6 :       GNUNET_free (dec);
     423              :     }
     424              :   }
     425           57 :   return TALER_EC_NONE;
     426              : }
     427              : 
     428              : 
     429              : /**
     430              :  * Checks if @a permission_required is in permissions of
     431              :  * @a scope.
     432              :  *
     433              :  * @param permission_required the permission to check.
     434              :  * @param scope the scope to check.
     435              :  * @return true if @a permission_required is in the permissions set of @a scope.
     436              :  */
     437              : static bool
     438          817 : permission_in_scope (const char *permission_required,
     439              :                      enum TMH_AuthScope scope)
     440              : {
     441              :   char *permissions;
     442              :   const char *perms_tmp;
     443          817 :   bool is_read_perm = false;
     444          817 :   bool is_write_perm = false;
     445              :   bool refreshable;
     446              :   const char *last_dash;
     447              : 
     448          817 :   perms_tmp = get_scope_permissions (scope,
     449              :                                      &refreshable);
     450          817 :   if (NULL == perms_tmp)
     451              :   {
     452           19 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     453              :                 "Permission check failed: scope %d not understood\n",
     454              :                 (int) scope);
     455           19 :     return false;
     456              :   }
     457          798 :   last_dash = strrchr (permission_required,
     458              :                        '-');
     459          798 :   if (NULL != last_dash)
     460              :   {
     461          797 :     is_write_perm = (0 == strcmp (last_dash,
     462              :                                   "-write"));
     463          797 :     is_read_perm = (0 == strcmp (last_dash,
     464              :                                  "-read"));
     465              :   }
     466              : 
     467          798 :   if (0 == strcmp ("token-refresh",
     468              :                    permission_required))
     469              :   {
     470           25 :     if (! refreshable)
     471              :     {
     472            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     473              :                   "Permission check failed: token not refreshable\n");
     474              :     }
     475           25 :     return refreshable;
     476              :   }
     477          773 :   permissions = GNUNET_strdup (perms_tmp);
     478              :   {
     479          773 :     const char *perm = strtok (permissions,
     480              :                                ",");
     481              : 
     482          773 :     if (NULL == perm)
     483              :     {
     484            0 :       GNUNET_free (permissions);
     485            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     486              :                   "Permission check failed: empty permission set\n");
     487            0 :       return false;
     488              :     }
     489          814 :     while (NULL != perm)
     490              :     {
     491          809 :       if (0 == strcmp ("*",
     492              :                        perm))
     493              :       {
     494          759 :         GNUNET_free (permissions);
     495          759 :         return true;
     496              :       }
     497           50 :       if ( (0 == strcmp ("*-write",
     498            0 :                          perm)) &&
     499              :            (is_write_perm) )
     500              :       {
     501            0 :         GNUNET_free (permissions);
     502            0 :         return true;
     503              :       }
     504           50 :       if ( (0 == strcmp ("*-read",
     505            4 :                          perm)) &&
     506              :            (is_read_perm) )
     507              :       {
     508            1 :         GNUNET_free (permissions);
     509            1 :         return true;
     510              :       }
     511           49 :       if (0 == strcmp (permission_required,
     512              :                        perm))
     513              :       {
     514            8 :         GNUNET_free (permissions);
     515            8 :         return true;
     516              :       }
     517           41 :       perm = strtok (NULL,
     518              :                      ",");
     519              :     }
     520              :   }
     521            5 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     522              :               "Permission check failed: %s not found in %s\n",
     523              :               permission_required,
     524              :               permissions);
     525            5 :   GNUNET_free (permissions);
     526            5 :   return false;
     527              : }
     528              : 
     529              : 
     530              : bool
     531           24 : TMH_scope_is_subset (enum TMH_AuthScope as,
     532              :                      enum TMH_AuthScope candidate)
     533              : {
     534              :   const char *as_perms;
     535              :   const char *candidate_perms;
     536              :   char *permissions;
     537              :   bool as_refreshable;
     538              :   bool cand_refreshable;
     539              : 
     540           24 :   as_perms = get_scope_permissions (as,
     541              :                                     &as_refreshable);
     542           24 :   candidate_perms = get_scope_permissions (candidate,
     543              :                                            &cand_refreshable);
     544           24 :   if (! as_refreshable && cand_refreshable)
     545            0 :     return false;
     546           24 :   if ( (NULL == as_perms) &&
     547              :        (NULL != candidate_perms) )
     548            0 :     return false;
     549           24 :   if ( (NULL == candidate_perms) ||
     550           24 :        (0 == strcmp ("*",
     551              :                      as_perms)))
     552           23 :     return true;
     553            1 :   permissions = GNUNET_strdup (candidate_perms);
     554              :   {
     555              :     const char *perm;
     556              : 
     557            1 :     perm = strtok (permissions,
     558              :                    ",");
     559            1 :     if (NULL == perm)
     560              :     {
     561            0 :       GNUNET_free (permissions);
     562            0 :       return true;
     563              :     }
     564            1 :     while (NULL != perm)
     565              :     {
     566            1 :       if (! permission_in_scope (perm,
     567              :                                  as))
     568              :       {
     569            1 :         GNUNET_free (permissions);
     570            1 :         return false;
     571              :       }
     572            0 :       perm = strtok (NULL,
     573              :                      ",");
     574              :     }
     575              :   }
     576            0 :   GNUNET_free (permissions);
     577            0 :   return true;
     578              : }
     579              : 
     580              : 
     581              : enum TMH_AuthScope
     582           24 : TMH_get_scope_by_name (const char *name)
     583              : {
     584           24 :   if (NULL == name)
     585            0 :     return TMH_AS_NONE;
     586           81 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     587              :   {
     588           81 :     if (0 == strcasecmp (scope_permissions[i].name,
     589              :                          name))
     590           24 :       return scope_permissions[i].as;
     591              :   }
     592            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     593              :               "Name `%s' does not match any scope we understand\n",
     594              :               name);
     595            0 :   return TMH_AS_NONE;
     596              : }
     597              : 
     598              : 
     599              : const char*
     600            2 : TMH_get_name_by_scope (enum TMH_AuthScope scope,
     601              :                        bool *refreshable)
     602              : {
     603            2 :   *refreshable = scope & TMH_AS_REFRESHABLE;
     604            8 :   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
     605              :   {
     606              :     /* We ignore the TMH_AS_REFRESHABLE bit */
     607            8 :     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
     608            8 :          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
     609            2 :       return scope_permissions[i].name;
     610              :   }
     611            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     612              :               "Scope #%d does not match any scope we understand\n",
     613              :               (int) scope);
     614            0 :   return NULL;
     615              : }
     616              : 
     617              : 
     618              : enum GNUNET_GenericReturnValue
     619           45 : TMH_check_auth (const char *password,
     620              :                 struct TALER_MerchantAuthenticationSaltP *salt,
     621              :                 struct TALER_MerchantAuthenticationHashP *hash)
     622              : {
     623              :   struct TALER_MerchantAuthenticationHashP val;
     624              : 
     625           45 :   if (GNUNET_is_zero (hash))
     626            0 :     return GNUNET_OK;
     627           45 :   if (NULL == password)
     628              :   {
     629            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     630              :                 "Denying access: empty password provided\n");
     631            1 :     return GNUNET_SYSERR;
     632              :   }
     633           44 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     634              :               "Checking against token with salt %s\n",
     635              :               TALER_B2S (salt));
     636           44 :   TALER_merchant_instance_auth_hash_with_salt (&val,
     637              :                                                salt,
     638              :                                                password);
     639           44 :   if (0 !=
     640           44 :       GNUNET_memcmp (&val,
     641              :                      hash))
     642              :   {
     643           12 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     644              :                 "Access denied: password does not match\n");
     645           12 :     return GNUNET_SYSERR;
     646              :   }
     647           32 :   return GNUNET_OK;
     648              : }
     649              : 
     650              : 
     651              : /**
     652              :  * Check if the client has provided the necessary credentials
     653              :  * to access the selected endpoint of the selected instance.
     654              :  *
     655              :  * @param[in,out] hc handler context
     656              :  * @return #GNUNET_OK on success,
     657              :  *         #GNUNET_NO if an error was queued (return #MHD_YES)
     658              :  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
     659              :  */
     660              : enum GNUNET_GenericReturnValue
     661          819 : TMH_perform_access_control (struct TMH_HandlerContext *hc)
     662              : {
     663              :   const char *auth;
     664          819 :   bool is_basic_auth = false;
     665          819 :   bool auth_malformed = false;
     666              : 
     667          819 :   auth = MHD_lookup_connection_value (hc->connection,
     668              :                                       MHD_HEADER_KIND,
     669              :                                       MHD_HTTP_HEADER_AUTHORIZATION);
     670              : 
     671          819 :   if (NULL != auth)
     672              :   {
     673          112 :     extract_auth (&auth,
     674              :                   &is_basic_auth);
     675          112 :     if (NULL == auth)
     676            0 :       auth_malformed = true;
     677          112 :     hc->auth_token = auth;
     678              :   }
     679              : 
     680              :   /* If we have zero configured instances (not even ones that have been
     681              :      purged) or explicitly disabled authentication, THEN we accept anything
     682              :      (no access control), as we then also have no data to protect. */
     683          819 :   if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
     684          794 :       (GNUNET_YES == TMH_auth_disabled))
     685              :   {
     686           25 :     hc->auth_scope = TMH_AS_ALL;
     687              :   }
     688          794 :   else if (is_basic_auth)
     689              :   {
     690           20 :     process_basic_auth (hc,
     691              :                         auth);
     692              :   }
     693              :   else   /* Check bearer token */
     694              :   {
     695              :     enum TALER_ErrorCode ec;
     696              : 
     697          774 :     ec = process_bearer_auth (hc,
     698              :                               auth);
     699          774 :     if (TALER_EC_NONE != ec)
     700              :     {
     701            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     702              :                   "Bearer authentication failed: %d\n",
     703              :                   (int) ec);
     704              :       return (MHD_YES ==
     705            0 :               TALER_MHD_reply_with_ec (hc->connection,
     706              :                                        ec,
     707              :                                        NULL))
     708              :           ? GNUNET_NO
     709            0 :           : GNUNET_SYSERR;
     710              :     }
     711              :   }
     712              :   /* We grant access if:
     713              :      - Endpoint does not require permissions
     714              :      - Authorization scope of bearer token contains permissions
     715              :        required by endpoint.
     716              :    */
     717          819 :   if ( (NULL != hc->rh->permission) &&
     718          816 :        (! permission_in_scope (hc->rh->permission,
     719              :                                hc->auth_scope)))
     720              :   {
     721           24 :     if (auth_malformed &&
     722            0 :         (TMH_AS_NONE == hc->auth_scope) )
     723              :     {
     724            0 :       GNUNET_break_op (0);
     725              :       return (MHD_YES ==
     726            0 :               TALER_MHD_reply_with_error (
     727              :                 hc->connection,
     728              :                 MHD_HTTP_UNAUTHORIZED,
     729              :                 TALER_EC_GENERIC_PARAMETER_MALFORMED,
     730              :                 "'" RFC_8959_PREFIX
     731              :                 "' prefix or 'Bearer' missing in 'Authorization' header"))
     732              :           ? GNUNET_NO
     733            0 :           : GNUNET_SYSERR;
     734              :     }
     735           24 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     736              :                 "Credentials provided are %d which are insufficient for access to `%s'\n",
     737              :                 (int) hc->auth_scope,
     738              :                 hc->rh->permission);
     739              :     return (MHD_YES ==
     740           24 :             TALER_MHD_reply_with_error (
     741              :               hc->connection,
     742              :               MHD_HTTP_UNAUTHORIZED,
     743              :               TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
     744              :               "Check credentials in 'Authorization' header"))
     745              :         ? GNUNET_NO
     746           24 :         : GNUNET_SYSERR;
     747              :   }
     748          795 :   return GNUNET_OK;
     749              : }
        

Generated by: LCOV version 2.0-1