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 : }
|