merchant

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

taler-merchant-httpd_auth.c (21175B)


      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   /* 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,pos-read,products-read,products-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,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 get_scope_permissions (enum TMH_AuthScope as,
    132                        bool *refreshable)
    133 {
    134   *refreshable = as & TMH_AS_REFRESHABLE;
    135   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    136   {
    137     /* We ignore the TMH_AS_REFRESHABLE bit */
    138     if ( (as & ~TMH_AS_REFRESHABLE)  ==
    139          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    140       return scope_permissions[i].permissions;
    141   }
    142   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    143               "Failed to find required permissions for scope %d\n",
    144               as);
    145   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 extract_auth (const char **auth,
    168               bool *is_basic_auth)
    169 {
    170   const char *bearer = "Bearer ";
    171   const char *basic = "Basic ";
    172   const char *tok = *auth;
    173   size_t offset = 0;
    174   bool is_bearer = false;
    175 
    176   *is_basic_auth = false;
    177   if (0 == strncmp (tok,
    178                     bearer,
    179                     strlen (bearer)))
    180   {
    181     offset = strlen (bearer);
    182     is_bearer = true;
    183   }
    184   else if (0 == strncmp (tok,
    185                          basic,
    186                          strlen (basic)))
    187   {
    188     offset = strlen (basic);
    189     *is_basic_auth = true;
    190   }
    191   else
    192   {
    193     *auth = NULL;
    194     return;
    195   }
    196   tok += offset;
    197   while (' ' == *tok)
    198     tok++;
    199   if ( (is_bearer) &&
    200        (0 != strncasecmp (tok,
    201                           RFC_8959_PREFIX,
    202                           strlen (RFC_8959_PREFIX))) )
    203   {
    204     *auth = NULL;
    205     return;
    206   }
    207   *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 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   const char *target_instance = "admin";
    227   enum GNUNET_GenericReturnValue ret;
    228 
    229   /* implicitly a zeroed out hash means no authentication */
    230   if (GNUNET_is_zero (&instance->auth.auth_hash))
    231     return GNUNET_OK;
    232   if (NULL == userpass)
    233   {
    234     GNUNET_break_op (0);
    235     return GNUNET_SYSERR;
    236   }
    237   if (0 ==
    238       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     GNUNET_break_op (0);
    245     GNUNET_free (tmp);
    246     return GNUNET_SYSERR;
    247   }
    248   colon = strchr (tmp,
    249                   ':');
    250   if (NULL == colon)
    251   {
    252     GNUNET_break_op (0);
    253     GNUNET_free (tmp);
    254     return GNUNET_SYSERR;
    255   }
    256   *colon = '\0';
    257   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   instance_name = GNUNET_STRINGS_utf8_tolower (tmp);
    264   /* instance->settings.id can be NULL if there is no instance yet */
    265   if (NULL != instance->settings.id)
    266     target_instance = instance->settings.id;
    267   if (0 != strcmp (instance_name,
    268                    target_instance))
    269   {
    270     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     GNUNET_free (instance_name);
    275     GNUNET_free (tmp);
    276     return GNUNET_SYSERR;
    277   }
    278   GNUNET_free (instance_name);
    279   ret = TMH_check_auth (password,
    280                         &instance->auth.auth_salt,
    281                         &instance->auth.auth_hash);
    282   GNUNET_free (tmp);
    283   if (GNUNET_OK != ret)
    284   {
    285     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    286                 "Password provided does not match credentials for %s\n",
    287                 target_instance);
    288   }
    289   return ret;
    290 }
    291 
    292 
    293 void
    294 TMH_compute_auth (const char *token,
    295                   struct TALER_MerchantAuthenticationSaltP *salt,
    296                   struct TALER_MerchantAuthenticationHashP *hash)
    297 {
    298   GNUNET_CRYPTO_random_block (salt,
    299                               sizeof (*salt));
    300   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    301               "Computing initial auth using token with salt %s\n",
    302               TALER_B2S (salt));
    303   TALER_merchant_instance_auth_hash_with_salt (hash,
    304                                                salt,
    305                                                token);
    306 }
    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 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   if ( (0 != strcmp (hc->rh->url_prefix,
    328                      "/token")) ||
    329        (NULL == hc->rh->method) ||
    330        (0 != strcmp (MHD_HTTP_METHOD_POST,
    331                      hc->rh->method)) ||
    332        (NULL == hc->instance))
    333   {
    334     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    335                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
    336                 hc->rh->url_prefix);
    337     hc->auth_scope = TMH_AS_NONE;
    338     return;
    339   }
    340   if (GNUNET_OK ==
    341       check_auth_instance (authn_s,
    342                            hc->instance))
    343   {
    344     hc->auth_scope = TMH_AS_ALL;
    345   }
    346   else
    347   {
    348     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    349                 "Basic authentication failed!\n");
    350     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 process_bearer_auth (struct TMH_HandlerContext *hc,
    366                      const char *authn_s)
    367 {
    368   if (NULL == hc->instance)
    369   {
    370     hc->auth_scope = TMH_AS_NONE;
    371     return TALER_EC_NONE;
    372   }
    373   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
    374   {
    375     /* hash zero means no authentication for instance */
    376     hc->auth_scope = TMH_AS_ALL;
    377     return TALER_EC_NONE;
    378   }
    379   {
    380     enum TALER_ErrorCode ec;
    381 
    382     ec = TMH_check_token (authn_s,
    383                           hc->instance->settings.id,
    384                           &hc->auth_scope);
    385     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       if (0 != strncasecmp (authn_s,
    393                             RFC_8959_PREFIX,
    394                             strlen (RFC_8959_PREFIX)))
    395       {
    396         GNUNET_break_op (0);
    397         hc->auth_scope = TMH_AS_NONE;
    398         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    399                     "Authentication token invalid: %d\n",
    400                     (int) ec);
    401         return ec;
    402       }
    403       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    404                   "Trying deprecated secret-token:password API authN\n");
    405       token = authn_s + strlen (RFC_8959_PREFIX);
    406       dec_len = GNUNET_STRINGS_urldecode (token,
    407                                           strlen (token),
    408                                           &dec);
    409       if ( (0 == dec_len) ||
    410            (GNUNET_OK !=
    411             TMH_check_auth (dec,
    412                             &hc->instance->auth.auth_salt,
    413                             &hc->instance->auth.auth_hash)) )
    414       {
    415         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    416                     "Login failed\n");
    417         hc->auth_scope = TMH_AS_NONE;
    418         GNUNET_free (dec);
    419         return TALER_EC_NONE;
    420       }
    421       hc->auth_scope = TMH_AS_ALL;
    422       GNUNET_free (dec);
    423     }
    424   }
    425   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 permission_in_scope (const char *permission_required,
    439                      enum TMH_AuthScope scope)
    440 {
    441   char *permissions;
    442   const char *perms_tmp;
    443   bool is_read_perm = false;
    444   bool is_write_perm = false;
    445   bool refreshable;
    446   const char *last_dash;
    447 
    448   perms_tmp = get_scope_permissions (scope,
    449                                      &refreshable);
    450   if (NULL == perms_tmp)
    451   {
    452     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    453                 "Permission check failed: scope %d not understood\n",
    454                 (int) scope);
    455     return false;
    456   }
    457   last_dash = strrchr (permission_required,
    458                        '-');
    459   if (NULL != last_dash)
    460   {
    461     is_write_perm = (0 == strcmp (last_dash,
    462                                   "-write"));
    463     is_read_perm = (0 == strcmp (last_dash,
    464                                  "-read"));
    465   }
    466 
    467   if (0 == strcmp ("token-refresh",
    468                    permission_required))
    469   {
    470     if (! refreshable)
    471     {
    472       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    473                   "Permission check failed: token not refreshable\n");
    474     }
    475     return refreshable;
    476   }
    477   permissions = GNUNET_strdup (perms_tmp);
    478   {
    479     const char *perm = strtok (permissions,
    480                                ",");
    481 
    482     if (NULL == perm)
    483     {
    484       GNUNET_free (permissions);
    485       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    486                   "Permission check failed: empty permission set\n");
    487       return false;
    488     }
    489     while (NULL != perm)
    490     {
    491       if (0 == strcmp ("*",
    492                        perm))
    493       {
    494         GNUNET_free (permissions);
    495         return true;
    496       }
    497       if ( (0 == strcmp ("*-write",
    498                          perm)) &&
    499            (is_write_perm) )
    500       {
    501         GNUNET_free (permissions);
    502         return true;
    503       }
    504       if ( (0 == strcmp ("*-read",
    505                          perm)) &&
    506            (is_read_perm) )
    507       {
    508         GNUNET_free (permissions);
    509         return true;
    510       }
    511       if (0 == strcmp (permission_required,
    512                        perm))
    513       {
    514         GNUNET_free (permissions);
    515         return true;
    516       }
    517       perm = strtok (NULL,
    518                      ",");
    519     }
    520   }
    521   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    522               "Permission check failed: %s not found in %s\n",
    523               permission_required,
    524               permissions);
    525   GNUNET_free (permissions);
    526   return false;
    527 }
    528 
    529 
    530 bool
    531 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   as_perms = get_scope_permissions (as,
    541                                     &as_refreshable);
    542   candidate_perms = get_scope_permissions (candidate,
    543                                            &cand_refreshable);
    544   if (! as_refreshable && cand_refreshable)
    545     return false;
    546   if ( (NULL == as_perms) &&
    547        (NULL != candidate_perms) )
    548     return false;
    549   if ( (NULL == candidate_perms) ||
    550        (0 == strcmp ("*",
    551                      as_perms)))
    552     return true;
    553   permissions = GNUNET_strdup (candidate_perms);
    554   {
    555     const char *perm;
    556 
    557     perm = strtok (permissions,
    558                    ",");
    559     if (NULL == perm)
    560     {
    561       GNUNET_free (permissions);
    562       return true;
    563     }
    564     while (NULL != perm)
    565     {
    566       if (! permission_in_scope (perm,
    567                                  as))
    568       {
    569         GNUNET_free (permissions);
    570         return false;
    571       }
    572       perm = strtok (NULL,
    573                      ",");
    574     }
    575   }
    576   GNUNET_free (permissions);
    577   return true;
    578 }
    579 
    580 
    581 enum TMH_AuthScope
    582 TMH_get_scope_by_name (const char *name)
    583 {
    584   if (NULL == name)
    585     return TMH_AS_NONE;
    586   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    587   {
    588     if (0 == strcasecmp (scope_permissions[i].name,
    589                          name))
    590       return scope_permissions[i].as;
    591   }
    592   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    593               "Name `%s' does not match any scope we understand\n",
    594               name);
    595   return TMH_AS_NONE;
    596 }
    597 
    598 
    599 const char*
    600 TMH_get_name_by_scope (enum TMH_AuthScope scope,
    601                        bool *refreshable)
    602 {
    603   *refreshable = scope & TMH_AS_REFRESHABLE;
    604   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    605   {
    606     /* We ignore the TMH_AS_REFRESHABLE bit */
    607     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
    608          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    609       return scope_permissions[i].name;
    610   }
    611   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    612               "Scope #%d does not match any scope we understand\n",
    613               (int) scope);
    614   return NULL;
    615 }
    616 
    617 
    618 enum GNUNET_GenericReturnValue
    619 TMH_check_auth (const char *password,
    620                 struct TALER_MerchantAuthenticationSaltP *salt,
    621                 struct TALER_MerchantAuthenticationHashP *hash)
    622 {
    623   struct TALER_MerchantAuthenticationHashP val;
    624 
    625   if (GNUNET_is_zero (hash))
    626     return GNUNET_OK;
    627   if (NULL == password)
    628   {
    629     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    630                 "Denying access: empty password provided\n");
    631     return GNUNET_SYSERR;
    632   }
    633   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    634               "Checking against token with salt %s\n",
    635               TALER_B2S (salt));
    636   TALER_merchant_instance_auth_hash_with_salt (&val,
    637                                                salt,
    638                                                password);
    639   if (0 !=
    640       GNUNET_memcmp (&val,
    641                      hash))
    642   {
    643     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    644                 "Access denied: password does not match\n");
    645     return GNUNET_SYSERR;
    646   }
    647   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 TMH_perform_access_control (struct TMH_HandlerContext *hc)
    662 {
    663   const char *auth;
    664   bool is_basic_auth = false;
    665   bool auth_malformed = false;
    666 
    667   auth = MHD_lookup_connection_value (hc->connection,
    668                                       MHD_HEADER_KIND,
    669                                       MHD_HTTP_HEADER_AUTHORIZATION);
    670 
    671   if (NULL != auth)
    672   {
    673     extract_auth (&auth,
    674                   &is_basic_auth);
    675     if (NULL == auth)
    676       auth_malformed = true;
    677     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   if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
    684       (GNUNET_YES == TMH_auth_disabled))
    685   {
    686     hc->auth_scope = TMH_AS_ALL;
    687   }
    688   else if (is_basic_auth)
    689   {
    690     process_basic_auth (hc,
    691                         auth);
    692   }
    693   else   /* Check bearer token */
    694   {
    695     enum TALER_ErrorCode ec;
    696 
    697     ec = process_bearer_auth (hc,
    698                               auth);
    699     if (TALER_EC_NONE != ec)
    700     {
    701       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    702                   "Bearer authentication failed: %d\n",
    703                   (int) ec);
    704       return (MHD_YES ==
    705               TALER_MHD_reply_with_ec (hc->connection,
    706                                        ec,
    707                                        NULL))
    708           ? GNUNET_NO
    709           : 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   if ( (NULL != hc->rh->permission) &&
    718        (! permission_in_scope (hc->rh->permission,
    719                                hc->auth_scope)))
    720   {
    721     if (auth_malformed &&
    722         (TMH_AS_NONE == hc->auth_scope) )
    723     {
    724       GNUNET_break_op (0);
    725       return (MHD_YES ==
    726               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           : GNUNET_SYSERR;
    734     }
    735     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             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         : GNUNET_SYSERR;
    747   }
    748   return GNUNET_OK;
    749 }