aboutsummaryrefslogtreecommitdiff
path: root/src/reclaim/plugin_rest_openid_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim/plugin_rest_openid_connect.c')
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c3162
1 files changed, 0 insertions, 3162 deletions
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
deleted file mode 100644
index ff882aca1..000000000
--- a/src/reclaim/plugin_rest_openid_connect.c
+++ /dev/null
@@ -1,3162 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @author Tristan Schwieren
24 * @file identity/plugin_rest_openid_connect.c
25 * @brief GNUnet Namestore REST plugin
26 *
27 */
28#include "platform.h"
29#include <inttypes.h>
30#include <jansson.h>
31#include <jose/jose.h>
32
33#include "gnunet_util_lib.h"
34#include "gnunet_gns_service.h"
35#include "gnunet_gnsrecord_lib.h"
36#include "gnunet_identity_service.h"
37#include "gnunet_namestore_service.h"
38#include "gnunet_reclaim_lib.h"
39#include "gnunet_reclaim_service.h"
40#include "gnunet_rest_lib.h"
41#include "gnunet_rest_plugin.h"
42#include "gnunet_signatures.h"
43#include "microhttpd.h"
44#include "oidc_helper.h"
45
46/**
47 * REST root namespace
48 */
49#define GNUNET_REST_API_NS_OIDC "/openid"
50
51/**
52 * OIDC config
53 */
54#define GNUNET_REST_API_NS_OIDC_CONFIG "/.well-known/openid-configuration"
55
56/**
57 * Authorize endpoint
58 */
59#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
60
61/**
62 * Token endpoint
63 */
64#define GNUNET_REST_API_NS_TOKEN "/openid/token"
65
66/**
67 * JSON Web Keys endpoint
68 */
69#define GNUNET_REST_API_JWKS "/jwks.json"
70
71/**
72 * UserInfo endpoint
73 */
74#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
75
76/**
77 * Login namespace
78 */
79#define GNUNET_REST_API_NS_LOGIN "/openid/login"
80
81/**
82 * State while collecting all egos
83 */
84#define ID_REST_STATE_INIT 0
85
86/**
87 * Done collecting egos
88 */
89#define ID_REST_STATE_POST_INIT 1
90
91/**
92 * OIDC grant_type key
93 */
94#define OIDC_GRANT_TYPE_KEY "grant_type"
95
96/**
97 * OIDC grant_type key
98 */
99#define OIDC_GRANT_TYPE_VALUE "authorization_code"
100
101/**
102 * OIDC code key
103 */
104#define OIDC_CODE_KEY "code"
105
106/**
107 * OIDC response_type key
108 */
109#define OIDC_RESPONSE_TYPE_KEY "response_type"
110
111/**
112 * OIDC client_id key
113 */
114#define OIDC_CLIENT_ID_KEY "client_id"
115
116/**
117 * OIDC scope key
118 */
119#define OIDC_SCOPE_KEY "scope"
120
121/**
122 * OIDC redirect_uri key
123 */
124#define OIDC_REDIRECT_URI_KEY "redirect_uri"
125
126/**
127 * OIDC state key
128 */
129#define OIDC_STATE_KEY "state"
130
131/**
132 * OIDC nonce key
133 */
134#define OIDC_NONCE_KEY "nonce"
135
136/**
137 * OIDC claims key
138 */
139#define OIDC_CLAIMS_KEY "claims"
140
141/**
142 * OIDC PKCE code challenge
143 */
144#define OIDC_CODE_CHALLENGE_KEY "code_challenge"
145
146/**
147 * OIDC PKCE code verifier
148 */
149#define OIDC_CODE_VERIFIER_KEY "code_verifier"
150
151/**
152 * OIDC cookie expiration (in seconds)
153 */
154#define OIDC_COOKIE_EXPIRATION 3
155
156/**
157 * OIDC cookie header key
158 */
159#define OIDC_COOKIE_HEADER_KEY "cookie"
160
161/**
162 * OIDC cookie header information key
163 */
164#define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
165
166/**
167 * OIDC cookie header information key
168 */
169#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
170
171/**
172 * OIDC cookie header if user cancelled
173 */
174#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
175
176/**
177 * OIDC expected response_type while authorizing
178 */
179#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
180
181/**
182 * OIDC expected scope part while authorizing
183 */
184#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
185
186/**
187 * OIDC error key for invalid client
188 */
189#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
190
191/**
192 * OIDC error key for invalid scopes
193 */
194#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
195
196/**
197 * OIDC error key for invalid requests
198 */
199#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
200
201/**
202 * OIDC error key for invalid tokens
203 */
204#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
205
206/**
207 * OIDC error key for invalid cookies
208 */
209#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
210
211/**
212 * OIDC error key for generic server errors
213 */
214#define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
215
216/**
217 * OIDC error key for unsupported grants
218 */
219#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
220
221/**
222 * OIDC error key for unsupported response types
223 */
224#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
225
226/**
227 * OIDC error key for unauthorized clients
228 */
229#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
230
231/**
232 * OIDC error key for denied access
233 */
234#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
235
236/**
237 * OIDC key store file name
238 */
239#define OIDC_JWK_RSA_FILENAME "jwk_rsa.json"
240
241/**
242 * How long to wait for a consume in userinfo endpoint
243 */
244#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
245 GNUNET_TIME_UNIT_SECONDS,2)
246
247/**
248 * OIDC ignored parameter array
249 */
250static char *OIDC_ignored_parameter_array[] = { "display",
251 "prompt",
252 "ui_locales",
253 "response_mode",
254 "id_token_hint",
255 "login_hint",
256 "acr_values" };
257
258/**
259 * OIDC hashmap for cached access tokens and codes
260 */
261struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache;
262
263/**
264 * OIDC hashmap that keeps track of issued cookies
265 */
266struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
267
268/**
269 * The configuration handle
270 */
271const struct GNUNET_CONFIGURATION_Handle *cfg;
272
273/**
274 * HTTP methods allows for this plugin
275 */
276static char *allow_methods;
277
278/**
279 * Ego list
280 */
281static struct EgoEntry *ego_head;
282
283/**
284 * Ego list
285 */
286static struct EgoEntry *ego_tail;
287
288/**
289 * The processing state
290 */
291static int state;
292
293/**
294 * Handle to Identity service.
295 */
296static struct GNUNET_IDENTITY_Handle *identity_handle;
297
298/**
299 * GNS handle
300 */
301static struct GNUNET_GNS_Handle *gns_handle;
302
303/**
304 * Identity Provider
305 */
306static struct GNUNET_RECLAIM_Handle *idp;
307
308/**
309 * Timeout for consume call on userinfo
310 */
311static struct GNUNET_TIME_Relative consume_timeout;
312
313/**
314 * @brief struct returned by the initialization function of the plugin
315 */
316struct Plugin
317{
318 const struct GNUNET_CONFIGURATION_Handle *cfg;
319};
320
321/**
322 * @brief The RSA key used by the oidc enpoint
323 */
324json_t *oidc_jwk;
325
326/**
327 * OIDC needed variables
328 */
329struct OIDC_Variables
330{
331 /**
332 * The RP client public key
333 */
334 struct GNUNET_IDENTITY_PublicKey client_pkey;
335
336 /**
337 * The OIDC client id of the RP
338 */
339 char *client_id;
340
341 /**
342 * The OIDC redirect uri
343 */
344 char *redirect_uri;
345
346 /**
347 * The list of oidc scopes
348 */
349 char *scope;
350
351 /**
352 * The OIDC state
353 */
354 char *state;
355
356 /**
357 * The OIDC nonce
358 */
359 char *nonce;
360
361 /**
362 * The OIDC claims
363 */
364 char *claims;
365
366 /**
367 * The OIDC response type
368 */
369 char *response_type;
370
371 /**
372 * The identity chosen by the user to login
373 */
374 char *login_identity;
375
376 /**
377 * User cancelled authorization/login
378 */
379 int user_cancelled;
380
381 /**
382 * The PKCE code_challenge
383 */
384 char *code_challenge;
385
386 /**
387 * The PKCE code_verifier
388 */
389 char *code_verifier;
390
391};
392
393/**
394 * The ego list
395 */
396struct EgoEntry
397{
398 /**
399 * DLL
400 */
401 struct EgoEntry *next;
402
403 /**
404 * DLL
405 */
406 struct EgoEntry *prev;
407
408 /**
409 * Ego Identifier
410 */
411 char *identifier;
412
413 /**
414 * Public key string
415 */
416 char *keystring;
417
418 /**
419 * The Ego
420 */
421 struct GNUNET_IDENTITY_Ego *ego;
422};
423
424
425struct RequestHandle
426{
427 /**
428 * DLL
429 */
430 struct RequestHandle *next;
431
432 /**
433 * DLL
434 */
435 struct RequestHandle *prev;
436
437 /**
438 * Selected ego
439 */
440 struct EgoEntry *ego_entry;
441
442 /**
443 * Pointer to ego private key
444 */
445 struct GNUNET_IDENTITY_PrivateKey priv_key;
446
447 /**
448 * OIDC variables
449 */
450 struct OIDC_Variables *oidc;
451
452 /**
453 * GNS lookup op
454 */
455 struct GNUNET_GNS_LookupRequest *gns_op;
456
457 /**
458 * Rest connection
459 */
460 struct GNUNET_REST_RequestHandle *rest_handle;
461
462 /**
463 * Attribute claim list for id_token
464 */
465 struct GNUNET_RECLAIM_AttributeList *attr_idtoken_list;
466
467 /**
468 * Attribute claim list for userinfo
469 */
470 struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list;
471
472 /**
473 * Credentials
474 */
475 struct GNUNET_RECLAIM_CredentialList *credentials;
476
477 /**
478 * Presentations
479 */
480 struct GNUNET_RECLAIM_PresentationList *presentations;
481
482 /**
483 * IDENTITY Operation
484 */
485 struct GNUNET_IDENTITY_Operation *op;
486
487
488 /**
489 * Idp Operation
490 */
491 struct GNUNET_RECLAIM_Operation *idp_op;
492
493 /**
494 * Timeout task for consume
495 */
496 struct GNUNET_SCHEDULER_Task *consume_timeout_op;
497
498 /**
499 * Attribute iterator
500 */
501 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
502
503 /**
504 * Credential iterator
505 */
506 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
507
508
509 /**
510 * Ticket iterator
511 */
512 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
513
514 /**
515 * A ticket
516 */
517 struct GNUNET_RECLAIM_Ticket ticket;
518
519 /**
520 * Desired timeout for the lookup (default is no timeout).
521 */
522 struct GNUNET_TIME_Relative timeout;
523
524 /**
525 * ID of a task associated with the resolution process.
526 */
527 struct GNUNET_SCHEDULER_Task *timeout_task;
528
529 /**
530 * The plugin result processor
531 */
532 GNUNET_REST_ResultProcessor proc;
533
534 /**
535 * The closure of the result processor
536 */
537 void *proc_cls;
538
539 /**
540 * The url
541 */
542 char *url;
543
544 /**
545 * The passed access token
546 */
547 char *access_token;
548
549 /**
550 * The tld for redirect
551 */
552 char *tld;
553
554 /**
555 * The redirect prefix
556 */
557 char *redirect_prefix;
558
559 /**
560 * The redirect suffix
561 */
562 char *redirect_suffix;
563
564 /**
565 * Error response message
566 */
567 char *emsg;
568
569 /**
570 * Error response description
571 */
572 char *edesc;
573
574 /**
575 * Response code
576 */
577 int response_code;
578
579 /**
580 * Public client
581 */
582 int public_client;
583};
584
585/**
586 * DLL
587 */
588static struct RequestHandle *requests_head;
589
590/**
591 * DLL
592 */
593static struct RequestHandle *requests_tail;
594
595
596/**
597 * Cleanup lookup handle
598 * @param handle Handle to clean up
599 */
600static void
601cleanup_handle (struct RequestHandle *handle)
602{
603
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
605 if (NULL != handle->timeout_task)
606 GNUNET_SCHEDULER_cancel (handle->timeout_task);
607 if (NULL != handle->attr_it)
608 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
609 if (NULL != handle->cred_it)
610 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
611 if (NULL != handle->ticket_it)
612 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
613 if (NULL != handle->idp_op)
614 GNUNET_RECLAIM_cancel (handle->idp_op);
615 if (NULL != handle->consume_timeout_op)
616 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
617 GNUNET_free (handle->url);
618 GNUNET_free (handle->tld);
619 GNUNET_free (handle->redirect_prefix);
620 GNUNET_free (handle->redirect_suffix);
621 GNUNET_free (handle->emsg);
622 GNUNET_free (handle->edesc);
623 if (NULL != handle->gns_op)
624 GNUNET_GNS_lookup_cancel (handle->gns_op);
625 if (NULL != handle->oidc)
626 {
627 GNUNET_free (handle->oidc->client_id);
628 GNUNET_free (handle->oidc->login_identity);
629 GNUNET_free (handle->oidc->nonce);
630 GNUNET_free (handle->oidc->redirect_uri);
631 GNUNET_free (handle->oidc->response_type);
632 GNUNET_free (handle->oidc->scope);
633 GNUNET_free (handle->oidc->state);
634 if (NULL != handle->oidc->claims)
635 GNUNET_free (handle->oidc->claims);
636 if (NULL != handle->oidc->code_challenge)
637 GNUNET_free (handle->oidc->code_challenge);
638 GNUNET_free (handle->oidc);
639 }
640 if (NULL!=handle->attr_idtoken_list)
641 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list);
642 if (NULL!=handle->attr_userinfo_list)
643 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list);
644 if (NULL!=handle->credentials)
645 GNUNET_RECLAIM_credential_list_destroy (handle->credentials);
646 if (NULL!=handle->presentations)
647 GNUNET_RECLAIM_presentation_list_destroy (handle->presentations);
648 GNUNET_CONTAINER_DLL_remove (requests_head,
649 requests_tail,
650 handle);
651 if (NULL != handle->access_token)
652 GNUNET_free (handle->access_token);
653 GNUNET_free (handle);
654}
655
656
657/**
658 * Task run on error, sends error message. Cleans up everything.
659 *
660 * @param cls the `struct RequestHandle`
661 */
662static void
663do_error (void *cls)
664{
665 struct RequestHandle *handle = cls;
666 struct MHD_Response *resp;
667 char *json_error;
668
669 GNUNET_asprintf (&json_error,
670 "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
671 handle->emsg,
672 (NULL != handle->edesc) ? handle->edesc : "",
673 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
674 (NULL != handle->oidc->state) ? handle->oidc->state : "",
675 (NULL != handle->oidc->state) ? "\"" : "");
676 if (0 == handle->response_code)
677 handle->response_code = MHD_HTTP_BAD_REQUEST;
678 resp = GNUNET_REST_create_response (json_error);
679 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
680 GNUNET_assert (MHD_NO !=
681 MHD_add_response_header (resp,
682 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
683 "Basic"));
684 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
685 MHD_HTTP_HEADER_CONTENT_TYPE,
686 "application/json"));
687 handle->proc (handle->proc_cls, resp, handle->response_code);
688 cleanup_handle (handle);
689 GNUNET_free (json_error);
690}
691
692
693/**
694 * Task run on error in userinfo endpoint, sends error header. Cleans up
695 * everything
696 *
697 * @param cls the `struct RequestHandle`
698 */
699static void
700do_userinfo_error (void *cls)
701{
702 struct RequestHandle *handle = cls;
703 struct MHD_Response *resp;
704 char *error;
705
706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707 "Error: %s\n", handle->edesc);
708 GNUNET_asprintf (&error,
709 "error=\"%s\", error_description=\"%s\"",
710 handle->emsg,
711 (NULL != handle->edesc) ? handle->edesc : "");
712 resp = GNUNET_REST_create_response ("");
713 GNUNET_assert (MHD_NO !=
714 MHD_add_response_header (resp,
715 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
716 "Bearer"));
717 handle->proc (handle->proc_cls, resp, handle->response_code);
718 cleanup_handle (handle);
719 GNUNET_free (error);
720}
721
722
723/**
724 * Task run on error, sends error message and redirects. Cleans up everything.
725 *
726 * @param cls the `struct RequestHandle`
727 */
728static void
729do_redirect_error (void *cls)
730{
731 struct RequestHandle *handle = cls;
732 struct MHD_Response *resp;
733 char *redirect;
734
735 GNUNET_asprintf (&redirect,
736 "%s?error=%s&error_description=%s%s%s",
737 handle->oidc->redirect_uri,
738 handle->emsg,
739 handle->edesc,
740 (NULL != handle->oidc->state) ? "&state=" : "",
741 (NULL != handle->oidc->state) ? handle->oidc->state : "");
742 resp = GNUNET_REST_create_response ("");
743 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
744 "Location", redirect));
745 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
746 cleanup_handle (handle);
747 GNUNET_free (redirect);
748}
749
750
751/**
752 * Task run on timeout, sends error message. Cleans up everything.
753 *
754 * @param cls the `struct RequestHandle`
755 */
756static void
757do_timeout (void *cls)
758{
759 struct RequestHandle *handle = cls;
760
761 handle->timeout_task = NULL;
762 do_error (handle);
763}
764
765
766/**
767 * Respond to OPTIONS request
768 *
769 * @param con_handle the connection handle
770 * @param url the url
771 * @param cls the RequestHandle
772 */
773static void
774options_cont (struct GNUNET_REST_RequestHandle *con_handle,
775 const char *url,
776 void *cls)
777{
778 struct MHD_Response *resp;
779 struct RequestHandle *handle = cls;
780
781 // For now, independent of path return all options
782 resp = GNUNET_REST_create_response (NULL);
783 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
784 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
785 cleanup_handle (handle);
786 return;
787}
788
789
790/**
791 * Interprets cookie header and pass its identity keystring to handle
792 */
793static void
794cookie_identity_interpretation (struct RequestHandle *handle)
795{
796 struct GNUNET_HashCode cache_key;
797 char *cookies;
798 struct GNUNET_TIME_Absolute current_time, *relog_time;
799 char delimiter[] = "; ";
800 char *tmp_cookies;
801 char *token;
802 char *value;
803
804 // gets identity of login try with cookie
805 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
806 strlen (OIDC_COOKIE_HEADER_KEY),
807 &cache_key);
808 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
809 ->header_param_map,
810 &cache_key))
811 {
812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
813 return;
814 }
815 // splits cookies and find 'Identity' cookie
816 tmp_cookies =
817 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
818 &cache_key);
819 cookies = GNUNET_strdup (tmp_cookies);
820 token = strtok (cookies, delimiter);
821 handle->oidc->user_cancelled = GNUNET_NO;
822 handle->oidc->login_identity = NULL;
823 if (NULL == token)
824 {
825 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826 "Unable to parse cookie: %s\n",
827 cookies);
828 GNUNET_free (cookies);
829 return;
830 }
831
832 while (NULL != token)
833 {
834 if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
835 {
836 handle->oidc->user_cancelled = GNUNET_YES;
837 GNUNET_free (cookies);
838 return;
839 }
840 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
841 break;
842 token = strtok (NULL, delimiter);
843 }
844 if (NULL == token)
845 {
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "No cookie value to process: %s\n",
848 cookies);
849 GNUNET_free (cookies);
850 return;
851 }
852 GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
853 if (GNUNET_NO ==
854 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
855 {
856 GNUNET_log (
857 GNUNET_ERROR_TYPE_WARNING,
858 "Found cookie `%s', but no corresponding expiration entry present...\n",
859 token);
860 GNUNET_free (cookies);
861 return;
862 }
863 relog_time =
864 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
865 current_time = GNUNET_TIME_absolute_get ();
866 // 30 min after old login -> redirect to login
867 if (current_time.abs_value_us > relog_time->abs_value_us)
868 {
869 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
870 "Found cookie `%s', but it is expired.\n",
871 token);
872 GNUNET_free (cookies);
873 return;
874 }
875 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
876 GNUNET_assert (NULL != value);
877 handle->oidc->login_identity = GNUNET_strdup (value);
878 GNUNET_free (cookies);
879}
880
881
882/**
883 * @brief Read the the JSON Web Key in the given file and return it.
884 * Return NULL and emit warning if JSON can not be decoded or the key is
885 * invalid
886 *
887 * @param filename the file to read the JWK from
888 * @return json_t* the reed JWK
889 */
890json_t *
891read_jwk_from_file (const char *filename)
892{
893 json_t *jwk;
894 json_error_t error;
895
896 jwk = json_load_file (filename, JSON_DECODE_ANY, &error);
897
898 if (! jwk)
899 {
900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
901 ("Could not read OIDC RSA key from config file; %s\n"),
902 error.text);
903 }
904
905 return jwk;
906}
907
908/**
909 * @brief Write the JWK to file. If unsuccessful emit warning
910 *
911 * @param filename the name of the file the JWK is writen to
912 * @param jwk the JWK that is going to be written
913 * @return int Return GNUNET_OK if write is sucessfull
914 */
915static int
916write_jwk_to_file (const char *filename,
917 json_t *jwk)
918{
919 if (json_dump_file (jwk, filename, JSON_INDENT (2)))
920 {
921 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
922 ("Could not write OIDC RSA key to file %s\n"),
923 filename);
924 return GNUNET_ERROR_TYPE_WARNING;
925 }
926 else
927 return GNUNET_OK;
928}
929
930/**
931 * @brief Generate a new RSA JSON Web Key
932 *
933 * @return json_t* the generated JWK
934 */
935json_t *
936generate_jwk ()
937{
938 json_t *jwk;
939 jwk = json_pack ("{s:s,s:i}", "kty", "RSA", "bits", 2048);
940 jose_jwk_gen (NULL, jwk);
941 json_incref (jwk);
942 return jwk;
943}
944
945/**
946 * Return the path to the oidc directory path
947 *
948 * @param cls the RequestHandle
949 */
950char *
951get_oidc_dir_path (void *cls)
952{
953 char *oidc_directory;
954 struct RequestHandle *handle = cls;
955
956 // Read OIDC directory from config
957 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
958 "reclaim-rest-plugin",
959 "oidc_dir",
960 &oidc_directory))
961 {
962 // Could not read Config file
963 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
964 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
965 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
966 GNUNET_SCHEDULER_add_now (&do_error, handle);
967 return NULL;
968 }
969
970 return oidc_directory;
971}
972
973/**
974 * Return the path to the RSA JWK key file
975 *
976 * @param cls the RequestHandle
977 */
978char *
979get_oidc_jwk_path (void *cls)
980{
981 char *oidc_directory;
982 char *oidc_jwk_path;
983
984 oidc_directory = get_oidc_dir_path (cls);
985
986 // Create path to file
987 GNUNET_asprintf (&oidc_jwk_path, "%s/%s", oidc_directory,
988 OIDC_JWK_RSA_FILENAME);
989
990 return oidc_jwk_path;
991}
992
993
994/**
995 * Redirects to login page stored in configuration file
996 */
997static void
998login_redirect (void *cls)
999{
1000 char *login_base_url;
1001 char *new_redirect;
1002 char *tmp;
1003 struct MHD_Response *resp;
1004 struct GNUNET_Buffer buf = { 0 };
1005 struct RequestHandle *handle = cls;
1006
1007 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1008 "reclaim-rest-plugin",
1009 "address",
1010 &login_base_url))
1011 {
1012 GNUNET_buffer_write_str (&buf, login_base_url);
1013 GNUNET_buffer_write_fstr (&buf,
1014 "?%s=%s",
1015 OIDC_RESPONSE_TYPE_KEY,
1016 handle->oidc->response_type);
1017 GNUNET_buffer_write_fstr (&buf,
1018 "&%s=%s",
1019 OIDC_CLIENT_ID_KEY,
1020 handle->oidc->client_id);
1021 GNUNET_STRINGS_urlencode (handle->oidc->redirect_uri,
1022 strlen (handle->oidc->redirect_uri),
1023 &tmp);
1024 GNUNET_buffer_write_fstr (&buf,
1025 "&%s=%s",
1026 OIDC_REDIRECT_URI_KEY,
1027 tmp);
1028 GNUNET_free (tmp);
1029 GNUNET_STRINGS_urlencode (handle->oidc->scope,
1030 strlen (handle->oidc->scope),
1031 &tmp);
1032 GNUNET_buffer_write_fstr (&buf,
1033 "&%s=%s",
1034 OIDC_SCOPE_KEY,
1035 tmp);
1036 GNUNET_free (tmp);
1037 if (NULL != handle->oidc->state)
1038 {
1039 GNUNET_STRINGS_urlencode (handle->oidc->state,
1040 strlen (handle->oidc->state),
1041 &tmp);
1042 GNUNET_buffer_write_fstr (&buf,
1043 "&%s=%s",
1044 OIDC_STATE_KEY,
1045 handle->oidc->state);
1046 GNUNET_free (tmp);
1047 }
1048 if (NULL != handle->oidc->code_challenge)
1049 {
1050 GNUNET_buffer_write_fstr (&buf,
1051 "&%s=%s",
1052 OIDC_CODE_CHALLENGE_KEY,
1053 handle->oidc->code_challenge);
1054 }
1055 if (NULL != handle->oidc->nonce)
1056 {
1057 GNUNET_buffer_write_fstr (&buf,
1058 "&%s=%s",
1059 OIDC_NONCE_KEY,
1060 handle->oidc->nonce);
1061 }
1062 if (NULL != handle->oidc->claims)
1063 {
1064 GNUNET_STRINGS_urlencode (handle->oidc->claims,
1065 strlen (handle->oidc->claims),
1066 &tmp);
1067 GNUNET_buffer_write_fstr (&buf,
1068 "&%s=%s",
1069 OIDC_CLAIMS_KEY,
1070 tmp);
1071 GNUNET_free (tmp);
1072 }
1073 new_redirect = GNUNET_buffer_reap_str (&buf);
1074 resp = GNUNET_REST_create_response ("");
1075 MHD_add_response_header (resp, "Location", new_redirect);
1076 GNUNET_free (login_base_url);
1077 }
1078 else
1079 {
1080 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1081 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1082 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1083 GNUNET_SCHEDULER_add_now (&do_error, handle);
1084 return;
1085 }
1086 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1087 GNUNET_free (new_redirect);
1088 cleanup_handle (handle);
1089}
1090
1091
1092/**
1093 * Does internal server error when iteration failed.
1094 */
1095static void
1096oidc_iteration_error (void *cls)
1097{
1098 struct RequestHandle *handle = cls;
1099
1100 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1101 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1102 GNUNET_SCHEDULER_add_now (&do_error, handle);
1103}
1104
1105
1106/**
1107 * Issues ticket and redirects to relying party with the authorization code as
1108 * parameter. Otherwise redirects with error
1109 */
1110static void
1111oidc_ticket_issue_cb (void *cls,
1112 const struct GNUNET_RECLAIM_Ticket *ticket,
1113 const struct
1114 GNUNET_RECLAIM_PresentationList *presentation)
1115{
1116 struct RequestHandle *handle = cls;
1117 struct MHD_Response *resp;
1118 char *ticket_str;
1119 char *redirect_uri;
1120 char *code_string;
1121
1122 handle->idp_op = NULL;
1123 if (NULL == ticket)
1124 {
1125 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1126 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
1127 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1128 return;
1129 }
1130 handle->ticket = *ticket;
1131 ticket_str =
1132 GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
1133 sizeof(struct GNUNET_RECLAIM_Ticket));
1134 code_string = OIDC_build_authz_code (&handle->priv_key,
1135 &handle->ticket,
1136 handle->attr_idtoken_list,
1137 presentation,
1138 handle->oidc->nonce,
1139 handle->oidc->code_challenge);
1140 if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
1141 (NULL != handle->tld))
1142 {
1143 GNUNET_asprintf (&redirect_uri,
1144 "%s.%s/%s%s%s=%s&state=%s",
1145 handle->redirect_prefix,
1146 handle->tld,
1147 handle->redirect_suffix,
1148 (NULL == strchr (handle->redirect_suffix, '?') ? "?" :
1149 "&"),
1150 handle->oidc->response_type,
1151 code_string,
1152 handle->oidc->state);
1153 }
1154 else
1155 {
1156 GNUNET_asprintf (&redirect_uri,
1157 "%s%s%s=%s&state=%s",
1158 handle->oidc->redirect_uri,
1159 (NULL == strchr (handle->oidc->redirect_uri, '?') ? "?" :
1160 "&"),
1161 handle->oidc->response_type,
1162 code_string,
1163 handle->oidc->state);
1164 }
1165 resp = GNUNET_REST_create_response ("");
1166 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1167 "Location", redirect_uri));
1168 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1169 cleanup_handle (handle);
1170 GNUNET_free (redirect_uri);
1171 GNUNET_free (ticket_str);
1172 GNUNET_free (code_string);
1173}
1174
1175
1176static struct GNUNET_RECLAIM_AttributeList*
1177attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a,
1178 struct GNUNET_RECLAIM_AttributeList *list_b)
1179{
1180 struct GNUNET_RECLAIM_AttributeList *merged_list;
1181 struct GNUNET_RECLAIM_AttributeListEntry *le_a;
1182 struct GNUNET_RECLAIM_AttributeListEntry *le_b;
1183 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1184
1185 merged_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1186 for (le_a = list_a->list_head; NULL != le_a; le_a = le_a->next)
1187 {
1188 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1189 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_a->attribute->name,
1190 &le_a->attribute->
1191 credential,
1192 le_a->attribute->type,
1193 le_a->attribute->data,
1194 le_a->attribute->data_size);
1195 le_m->attribute->id = le_a->attribute->id;
1196 le_m->attribute->flag = le_a->attribute->flag;
1197 le_m->attribute->credential = le_a->attribute->credential;
1198 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1199 merged_list->list_tail,
1200 le_m);
1201 }
1202 le_m = NULL;
1203 for (le_b = list_b->list_head; NULL != le_b; le_b = le_b->next)
1204 {
1205 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1206 {
1207 if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&le_m->attribute->id,
1208 &le_b->attribute->id))
1209 break; /** Attribute already in list **/
1210 }
1211 if (NULL != le_m)
1212 continue; /** Attribute already in list **/
1213 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1214 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_b->attribute->name,
1215 &le_b->attribute->
1216 credential,
1217 le_b->attribute->type,
1218 le_b->attribute->data,
1219 le_b->attribute->data_size);
1220 le_m->attribute->id = le_b->attribute->id;
1221 le_m->attribute->flag = le_b->attribute->flag;
1222 le_m->attribute->credential = le_b->attribute->credential;
1223 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1224 merged_list->list_tail,
1225 le_m);
1226 }
1227 return merged_list;
1228}
1229
1230
1231static void
1232oidc_cred_collect_finished_cb (void *cls)
1233{
1234 struct RequestHandle *handle = cls;
1235 struct GNUNET_RECLAIM_AttributeList *merged_list;
1236 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1237
1238 handle->cred_it = NULL;
1239 merged_list = attribute_list_merge (handle->attr_idtoken_list,
1240 handle->attr_userinfo_list);
1241 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1243 "List Attribute in ticket to issue: %s\n",
1244 le_m->attribute->name);
1245 handle->idp_op = GNUNET_RECLAIM_ticket_issue (idp,
1246 &handle->priv_key,
1247 &handle->oidc->client_pkey,
1248 merged_list,
1249 &oidc_ticket_issue_cb,
1250 handle);
1251 GNUNET_RECLAIM_attribute_list_destroy (merged_list);
1252}
1253
1254
1255/**
1256 * Collects all attributes for an ego if in scope parameter
1257 */
1258static void
1259oidc_cred_collect (void *cls,
1260 const struct GNUNET_IDENTITY_PublicKey *identity,
1261 const struct GNUNET_RECLAIM_Credential *cred)
1262{
1263 struct RequestHandle *handle = cls;
1264 struct GNUNET_RECLAIM_AttributeListEntry *le;
1265 struct GNUNET_RECLAIM_CredentialListEntry *ale;
1266
1267 for (ale = handle->credentials->list_head; NULL != ale; ale = ale->next)
1268 {
1269 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id,
1270 &cred->id))
1271 continue;
1272 /** Credential already in list **/
1273 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1274 return;
1275 }
1276
1277 for (le = handle->attr_idtoken_list->list_head; NULL != le; le = le->next)
1278 {
1279 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->credential,
1280 &cred->id))
1281 continue;
1282 /** Credential matches for attribute, add **/
1283 ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry);
1284 ale->credential = GNUNET_RECLAIM_credential_new (cred->name,
1285 cred->type,
1286 cred->data,
1287 cred->data_size);
1288 GNUNET_CONTAINER_DLL_insert (handle->credentials->list_head,
1289 handle->credentials->list_tail,
1290 ale);
1291 }
1292 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1293}
1294
1295
1296static void
1297oidc_attr_collect_finished_cb (void *cls)
1298{
1299 struct RequestHandle *handle = cls;
1300
1301 handle->attr_it = NULL;
1302 handle->ticket_it = NULL;
1303 if (NULL == handle->attr_idtoken_list->list_head)
1304 {
1305 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1306 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1307 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1308 return;
1309 }
1310 handle->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList);
1311 handle->cred_it =
1312 GNUNET_RECLAIM_get_credentials_start (idp,
1313 &handle->priv_key,
1314 &oidc_iteration_error,
1315 handle,
1316 &oidc_cred_collect,
1317 handle,
1318 &oidc_cred_collect_finished_cb,
1319 handle);
1320
1321}
1322
1323
1324static int
1325attr_in_claims_request (struct RequestHandle *handle,
1326 const char *attr_name,
1327 const char *claims_parameter)
1328{
1329 int ret = GNUNET_NO;
1330 json_t *root;
1331 json_error_t error;
1332 json_t *claims_j;
1333 const char *key;
1334 json_t *value;
1335
1336 /** Check if attribute is requested through a scope **/
1337 if (GNUNET_YES == OIDC_check_scopes_for_claim_request (handle->oidc->scope,
1338 attr_name))
1339 return GNUNET_YES;
1340
1341 /** Try claims parameter if not in scope */
1342 if (NULL != handle->oidc->claims)
1343 {
1344 root = json_loads (handle->oidc->claims, JSON_DECODE_ANY, &error);
1345 claims_j = json_object_get (root, claims_parameter);
1346 /* obj is a JSON object */
1347 if (NULL != claims_j)
1348 {
1349 json_object_foreach (claims_j, key, value) {
1350 if (0 != strcmp (attr_name, key))
1351 continue;
1352 ret = GNUNET_YES;
1353 break;
1354 }
1355 }
1356 json_decref (root);
1357 }
1358 return ret;
1359}
1360
1361
1362static int
1363attr_in_idtoken_request (struct RequestHandle *handle,
1364 const char *attr_name)
1365{
1366 return attr_in_claims_request (handle, attr_name, "id_token");
1367}
1368
1369
1370static int
1371attr_in_userinfo_request (struct RequestHandle *handle,
1372 const char *attr_name)
1373{
1374 return attr_in_claims_request (handle, attr_name, "userinfo");
1375}
1376
1377
1378/**
1379 * Collects all attributes for an ego if in scope parameter
1380 */
1381static void
1382oidc_attr_collect (void *cls,
1383 const struct GNUNET_IDENTITY_PublicKey *identity,
1384 const struct GNUNET_RECLAIM_Attribute *attr)
1385{
1386 struct RequestHandle *handle = cls;
1387 struct GNUNET_RECLAIM_AttributeListEntry *le;
1388 if (GNUNET_YES == attr_in_idtoken_request (handle, attr->name))
1389 {
1390 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1391 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1392 &attr->credential,
1393 attr->type,
1394 attr->data,
1395 attr->data_size);
1396 le->attribute->id = attr->id;
1397 le->attribute->flag = attr->flag;
1398 le->attribute->credential = attr->credential;
1399 GNUNET_CONTAINER_DLL_insert (handle->attr_idtoken_list->list_head,
1400 handle->attr_idtoken_list->list_tail,
1401 le);
1402 }
1403 if (GNUNET_YES == attr_in_userinfo_request (handle, attr->name))
1404 {
1405 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1406 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1407 &attr->credential,
1408 attr->type,
1409 attr->data,
1410 attr->data_size);
1411 le->attribute->id = attr->id;
1412 le->attribute->flag = attr->flag;
1413 le->attribute->credential = attr->credential;
1414 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
1415 handle->attr_userinfo_list->list_tail,
1416 le);
1417 }
1418
1419 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1420}
1421
1422
1423/**
1424 * Checks time and cookie and redirects accordingly
1425 */
1426static void
1427code_redirect (void *cls)
1428{
1429 struct RequestHandle *handle = cls;
1430 struct GNUNET_TIME_Absolute current_time;
1431 struct GNUNET_TIME_Absolute *relog_time;
1432 struct GNUNET_IDENTITY_PublicKey pubkey;
1433 struct GNUNET_IDENTITY_PublicKey ego_pkey;
1434 struct GNUNET_HashCode cache_key;
1435 char *identity_cookie;
1436
1437 GNUNET_asprintf (&identity_cookie,
1438 "Identity=%s",
1439 handle->oidc->login_identity);
1440 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1441 GNUNET_free (identity_cookie);
1442 // No login time for identity -> redirect to login
1443 if (GNUNET_YES ==
1444 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1445 {
1446 relog_time =
1447 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1448 current_time = GNUNET_TIME_absolute_get ();
1449 // 30 min after old login -> redirect to login
1450 if (current_time.abs_value_us <= relog_time->abs_value_us)
1451 {
1452 if (GNUNET_OK !=
1453 GNUNET_IDENTITY_public_key_from_string (handle->oidc
1454 ->login_identity,
1455 &pubkey))
1456 {
1457 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1458 handle->edesc =
1459 GNUNET_strdup ("The cookie of a login identity is not valid");
1460 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1461 return;
1462 }
1463 // iterate over egos and compare their public key
1464 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
1465 handle->ego_entry = handle->ego_entry->next)
1466 {
1467 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1468 if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1469 {
1470 handle->priv_key =
1471 *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1472 handle->attr_idtoken_list =
1473 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1474 handle->attr_userinfo_list =
1475 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1476 handle->attr_it =
1477 GNUNET_RECLAIM_get_attributes_start (idp,
1478 &handle->priv_key,
1479 &oidc_iteration_error,
1480 handle,
1481 &oidc_attr_collect,
1482 handle,
1483 &oidc_attr_collect_finished_cb,
1484 handle);
1485 return;
1486 }
1487 }
1488 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1489 return;
1490 }
1491 }
1492}
1493
1494
1495static void
1496build_redirect (void *cls)
1497{
1498 struct RequestHandle *handle = cls;
1499 struct MHD_Response *resp;
1500 char *redirect_uri;
1501
1502 if (GNUNET_YES == handle->oidc->user_cancelled)
1503 {
1504 if ((NULL != handle->redirect_prefix) &&
1505 (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1506 {
1507 GNUNET_asprintf (&redirect_uri,
1508 "%s.%s/%s?error=%s&error_description=%s&state=%s",
1509 handle->redirect_prefix,
1510 handle->tld,
1511 handle->redirect_suffix,
1512 "access_denied",
1513 "User denied access",
1514 handle->oidc->state);
1515 }
1516 else
1517 {
1518 GNUNET_asprintf (&redirect_uri,
1519 "%s?error=%s&error_description=%s&state=%s",
1520 handle->oidc->redirect_uri,
1521 "access_denied",
1522 "User denied access",
1523 handle->oidc->state);
1524 }
1525 resp = GNUNET_REST_create_response ("");
1526 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1527 "Location",
1528 redirect_uri));
1529 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1530 cleanup_handle (handle);
1531 GNUNET_free (redirect_uri);
1532 return;
1533 }
1534 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1535}
1536
1537
1538static void
1539lookup_redirect_uri_result (void *cls,
1540 uint32_t rd_count,
1541 const struct GNUNET_GNSRECORD_Data *rd)
1542{
1543 struct RequestHandle *handle = cls;
1544 char *tmp;
1545 char *tmp_key_str;
1546 char *pos;
1547 struct GNUNET_IDENTITY_PublicKey redirect_zone;
1548
1549 handle->gns_op = NULL;
1550 if (0 == rd_count)
1551 {
1552 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1553 handle->edesc =
1554 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1555 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1556 return;
1557 }
1558 for (int i = 0; i < rd_count; i++)
1559 {
1560 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1561 continue;
1562 if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1563 continue;
1564 tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1565 if (NULL == strstr (tmp, handle->oidc->client_id))
1566 {
1567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1568 "Redirect uri %s does not contain client_id %s\n",
1569 tmp,
1570 handle->oidc->client_id);
1571 }
1572 else
1573 {
1574 pos = strrchr (tmp, (unsigned char) '.');
1575 if (NULL == pos)
1576 {
1577 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1578 "Redirect uri %s contains client_id but is malformed\n",
1579 tmp);
1580 GNUNET_free (tmp);
1581 continue;
1582 }
1583 *pos = '\0';
1584 handle->redirect_prefix = GNUNET_strdup (tmp);
1585 tmp_key_str = pos + 1;
1586 pos = strchr (tmp_key_str, (unsigned char) '/');
1587 if (NULL == pos)
1588 {
1589 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1590 "Redirect uri %s contains client_id but is malformed\n",
1591 tmp);
1592 GNUNET_free (tmp);
1593 continue;
1594 }
1595 *pos = '\0';
1596 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1597
1598 GNUNET_STRINGS_string_to_data (tmp_key_str,
1599 strlen (tmp_key_str),
1600 &redirect_zone,
1601 sizeof(redirect_zone));
1602 }
1603 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1604 GNUNET_free (tmp);
1605 return;
1606 }
1607 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1608 handle->edesc =
1609 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1610 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1611}
1612
1613
1614/**
1615 * Initiate redirect back to client.
1616 */
1617static void
1618client_redirect (void *cls)
1619{
1620 struct RequestHandle *handle = cls;
1621
1622 /* Lookup client redirect uri to verify request */
1623 handle->gns_op =
1624 GNUNET_GNS_lookup (gns_handle,
1625 GNUNET_GNS_EMPTY_LABEL_AT,
1626 &handle->oidc->client_pkey,
1627 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1628 GNUNET_GNS_LO_DEFAULT,
1629 &lookup_redirect_uri_result,
1630 handle);
1631}
1632
1633
1634static char *
1635get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1636{
1637 struct GNUNET_HashCode hc;
1638 char *value;
1639 char *res;
1640
1641 GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1642 if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1643 ->url_param_map,
1644 &hc))
1645 return NULL;
1646 value =
1647 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1648 if (NULL == value)
1649 return NULL;
1650 GNUNET_STRINGS_urldecode (value, strlen (value), &res);
1651 return res;
1652}
1653
1654
1655/**
1656 * Iteration over all results finished, build final
1657 * response.
1658 *
1659 * @param cls the `struct RequestHandle`
1660 */
1661static void
1662build_authz_response (void *cls)
1663{
1664 struct RequestHandle *handle = cls;
1665 struct GNUNET_HashCode cache_key;
1666
1667 char *expected_scope;
1668 char delimiter[] = " ";
1669 int number_of_ignored_parameter, iterator;
1670
1671
1672 // REQUIRED value: redirect_uri
1673 handle->oidc->redirect_uri =
1674 get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1675 if (NULL == handle->oidc->redirect_uri)
1676 {
1677 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1678 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1679 GNUNET_SCHEDULER_add_now (&do_error, handle);
1680 return;
1681 }
1682
1683 // REQUIRED value: response_type
1684 handle->oidc->response_type =
1685 get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1686 if (NULL == handle->oidc->response_type)
1687 {
1688 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1689 handle->edesc = GNUNET_strdup ("missing parameter response_type");
1690 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1691 return;
1692 }
1693
1694 // REQUIRED value: scope
1695 handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1696 if (NULL == handle->oidc->scope)
1697 {
1698 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1699 handle->edesc = GNUNET_strdup ("missing parameter scope");
1700 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1701 return;
1702 }
1703
1704 // OPTIONAL value: nonce
1705 handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1706
1707 // OPTIONAL value: claims
1708 handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1709
1710 // TODO check other values if needed
1711 number_of_ignored_parameter =
1712 sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1713 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1714 {
1715 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1716 strlen (OIDC_ignored_parameter_array[iterator]),
1717 &cache_key);
1718 if (GNUNET_YES ==
1719 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1720 ->url_param_map,
1721 &cache_key))
1722 {
1723 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1724 GNUNET_asprintf (&handle->edesc,
1725 "Server will not handle parameter: %s",
1726 OIDC_ignored_parameter_array[iterator]);
1727 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1728 return;
1729 }
1730 }
1731
1732 // We only support authorization code flows.
1733 if (0 != strcmp (handle->oidc->response_type,
1734 OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1735 {
1736 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1737 handle->edesc = GNUNET_strdup ("The authorization server does not support "
1738 "obtaining this authorization code.");
1739 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1740 return;
1741 }
1742
1743 // Checks if scope contains 'openid'
1744 expected_scope = GNUNET_strdup (handle->oidc->scope);
1745 char *test;
1746 test = strtok (expected_scope, delimiter);
1747 while (NULL != test)
1748 {
1749 if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1750 break;
1751 test = strtok (NULL, delimiter);
1752 }
1753 if (NULL == test)
1754 {
1755 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1756 handle->edesc =
1757 GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1758 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1759 GNUNET_free (expected_scope);
1760 return;
1761 }
1762
1763 GNUNET_free (expected_scope);
1764 if ((NULL == handle->oidc->login_identity) &&
1765 (GNUNET_NO == handle->oidc->user_cancelled))
1766 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1767 else
1768 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1769}
1770
1771
1772/**
1773 * Iterate over tlds in config
1774 */
1775static void
1776tld_iter (void *cls, const char *section, const char *option, const char *value)
1777{
1778 struct RequestHandle *handle = cls;
1779 struct GNUNET_IDENTITY_PublicKey pkey;
1780
1781 if (GNUNET_OK !=
1782 GNUNET_IDENTITY_public_key_from_string (value, &pkey))
1783 {
1784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1785 return;
1786 }
1787 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1788 handle->tld = GNUNET_strdup (option + 1);
1789}
1790
1791
1792/**
1793 * Responds to authorization GET and url-encoded POST request
1794 *
1795 * @param con_handle the connection handle
1796 * @param url the url
1797 * @param cls the RequestHandle
1798 */
1799static void
1800authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1801 const char *url,
1802 void *cls)
1803{
1804 struct RequestHandle *handle = cls;
1805 struct EgoEntry *tmp_ego;
1806 const struct GNUNET_IDENTITY_PrivateKey *priv_key;
1807 struct GNUNET_IDENTITY_PublicKey pkey;
1808
1809 cookie_identity_interpretation (handle);
1810
1811 // RECOMMENDED value: state - REQUIRED for answers
1812 handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1813
1814 // REQUIRED value: client_id
1815 handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1816 if (NULL == handle->oidc->client_id)
1817 {
1818 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1819 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1820 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1821 GNUNET_SCHEDULER_add_now (&do_error, handle);
1822 return;
1823 }
1824
1825 // OPTIONAL value: code_challenge
1826 handle->oidc->code_challenge = get_url_parameter_copy (handle,
1827 OIDC_CODE_CHALLENGE_KEY);
1828 if (NULL == handle->oidc->code_challenge)
1829 {
1830 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1831 "OAuth authorization request does not contain PKCE parameters!\n");
1832 }
1833
1834 if (GNUNET_OK !=
1835 GNUNET_IDENTITY_public_key_from_string (handle->oidc->client_id,
1836 &handle->oidc->client_pkey))
1837 {
1838 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1839 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1840 "authorization code using this method.");
1841 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1842 GNUNET_SCHEDULER_add_now (&do_error, handle);
1843 return;
1844 }
1845
1846 // If we know this identity, translated the corresponding TLD
1847 // TODO: We might want to have a reverse lookup functionality for TLDs?
1848 for (tmp_ego = ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1849 {
1850 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1851 GNUNET_IDENTITY_key_get_public (priv_key, &pkey);
1852 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1853 {
1854 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1855 handle->ego_entry = ego_tail;
1856 }
1857 }
1858 if (NULL == handle->tld)
1859 GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1860 if (NULL == handle->tld)
1861 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1862 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1863}
1864
1865
1866/**
1867 * Combines an identity with a login time and responds OK to login request
1868 *
1869 * @param con_handle the connection handle
1870 * @param url the url
1871 * @param cls the RequestHandle
1872 */
1873static void
1874login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1875 const char *url,
1876 void *cls)
1877{
1878 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1879 struct RequestHandle *handle = cls;
1880 struct GNUNET_HashCode cache_key;
1881 struct GNUNET_TIME_Absolute *current_time;
1882 struct GNUNET_TIME_Absolute *last_time;
1883 char *cookie;
1884 char *header_val;
1885 json_t *root;
1886 json_error_t error;
1887 json_t *identity;
1888 char term_data[handle->rest_handle->data_size + 1];
1889
1890 term_data[handle->rest_handle->data_size] = '\0';
1891 GNUNET_memcpy (term_data,
1892 handle->rest_handle->data,
1893 handle->rest_handle->data_size);
1894 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1895 identity = json_object_get (root, "identity");
1896 if (! json_is_string (identity))
1897 {
1898 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1899 "Error parsing json string from %s\n",
1900 term_data);
1901 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1902 json_decref (root);
1903 cleanup_handle (handle);
1904 return;
1905 }
1906 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1907 GNUNET_asprintf (&header_val,
1908 "%s;Max-Age=%d",
1909 cookie,
1910 OIDC_COOKIE_EXPIRATION);
1911 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1912 "Set-Cookie", header_val));
1913 GNUNET_assert (MHD_NO !=
1914 MHD_add_response_header (resp,
1915 "Access-Control-Allow-Methods",
1916 "POST"));
1917 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1918
1919 if (0 != strcmp (json_string_value (identity), "Denied"))
1920 {
1921 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1922 *current_time = GNUNET_TIME_relative_to_absolute (
1923 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1924 OIDC_COOKIE_EXPIRATION));
1925 last_time =
1926 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1927 GNUNET_free (last_time);
1928 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1929 &cache_key,
1930 current_time,
1931 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1932 }
1933 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1934 GNUNET_free (cookie);
1935 GNUNET_free (header_val);
1936 json_decref (root);
1937 cleanup_handle (handle);
1938}
1939
1940
1941static int
1942parse_credentials_basic_auth (struct RequestHandle *handle,
1943 char **client_id,
1944 char **client_secret)
1945{
1946 struct GNUNET_HashCode cache_key;
1947 char *authorization;
1948 char *credentials;
1949 char *basic_authorization;
1950 char *client_id_tmp;
1951 char *pass;
1952
1953 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1954 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1955 &cache_key);
1956 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1957 ->header_param_map,
1958 &cache_key))
1959 return GNUNET_SYSERR;
1960 authorization =
1961 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1962 &cache_key);
1963
1964 // split header in "Basic" and [content]
1965 credentials = strtok (authorization, " ");
1966 if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1967 return GNUNET_SYSERR;
1968 credentials = strtok (NULL, " ");
1969 if (NULL == credentials)
1970 return GNUNET_SYSERR;
1971 GNUNET_STRINGS_base64_decode (credentials,
1972 strlen (credentials),
1973 (void **) &basic_authorization);
1974
1975 if (NULL == basic_authorization)
1976 return GNUNET_SYSERR;
1977 client_id_tmp = strtok (basic_authorization, ":");
1978 if (NULL == client_id_tmp)
1979 {
1980 GNUNET_free (basic_authorization);
1981 return GNUNET_SYSERR;
1982 }
1983 pass = strtok (NULL, ":");
1984 if (NULL == pass)
1985 {
1986 GNUNET_free (basic_authorization);
1987 return GNUNET_SYSERR;
1988 }
1989 *client_id = strdup (client_id_tmp);
1990 *client_secret = strdup (pass);
1991 GNUNET_free (basic_authorization);
1992 return GNUNET_OK;
1993}
1994
1995
1996static int
1997parse_credentials_post_body (struct RequestHandle *handle,
1998 char **client_id,
1999 char **client_secret)
2000{
2001 struct GNUNET_HashCode cache_key;
2002 char *client_id_tmp;
2003 char *pass;
2004
2005 GNUNET_CRYPTO_hash ("client_id",
2006 strlen ("client_id"),
2007 &cache_key);
2008 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2009 ->url_param_map,
2010 &cache_key))
2011 return GNUNET_SYSERR;
2012 client_id_tmp = GNUNET_CONTAINER_multihashmap_get (
2013 handle->rest_handle->url_param_map,
2014 &cache_key);
2015 if (NULL == client_id_tmp)
2016 return GNUNET_SYSERR;
2017 *client_id = strdup (client_id_tmp);
2018 GNUNET_CRYPTO_hash ("client_secret",
2019 strlen ("client_secret"),
2020 &cache_key);
2021 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2022 ->url_param_map,
2023 &cache_key))
2024 {
2025 GNUNET_free (*client_id);
2026 *client_id = NULL;
2027 return GNUNET_SYSERR;
2028 }
2029 pass = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
2030 &cache_key);
2031 if (NULL == pass)
2032 {
2033 GNUNET_free (*client_id);
2034 *client_id = NULL;
2035 return GNUNET_SYSERR;
2036 }
2037 *client_secret = strdup (pass);
2038 return GNUNET_OK;
2039}
2040
2041
2042static int
2043check_authorization (struct RequestHandle *handle,
2044 struct GNUNET_IDENTITY_PublicKey *cid)
2045{
2046 char *expected_pass;
2047 char *received_cid;
2048 char *received_cpw;
2049 char *pkce_cv;
2050
2051 if (GNUNET_OK == parse_credentials_basic_auth (handle,
2052 &received_cid,
2053 &received_cpw))
2054 {
2055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2056 "Received client credentials in HTTP AuthZ header\n");
2057 }
2058 else if (GNUNET_OK == parse_credentials_post_body (handle,
2059 &received_cid,
2060 &received_cpw))
2061 {
2062 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2063 "Received client credentials in POST body\n");
2064 }
2065 else
2066 {
2067 /** Allow public clients with PKCE **/
2068 pkce_cv = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2069 if (NULL == pkce_cv)
2070 {
2071 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2072 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2073 return GNUNET_SYSERR;
2074 }
2075 handle->public_client = GNUNET_YES;
2076 GNUNET_free (pkce_cv);
2077 received_cid = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
2078 GNUNET_STRINGS_string_to_data (received_cid,
2079 strlen (received_cid),
2080 cid,
2081 sizeof(struct GNUNET_IDENTITY_PublicKey));
2082 GNUNET_free (received_cid);
2083 return GNUNET_OK;
2084
2085 }
2086
2087 // check client password
2088 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
2089 "reclaim-rest-plugin",
2090 "OIDC_CLIENT_HMAC_SECRET",
2091 &expected_pass))
2092 {
2093 if (0 != strcmp (expected_pass, received_cpw))
2094 {
2095 GNUNET_free (expected_pass);
2096 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2097 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2098 GNUNET_free (received_cpw);
2099 GNUNET_free (received_cid);
2100 return GNUNET_SYSERR;
2101 }
2102 GNUNET_free (expected_pass);
2103 }
2104 else
2105 {
2106 GNUNET_free (received_cpw);
2107 GNUNET_free (received_cid);
2108 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2109 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2110 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2111 return GNUNET_SYSERR;
2112 }
2113 // check client_id
2114 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
2115 handle->ego_entry = handle->ego_entry->next)
2116 {
2117 if (0 == strcmp (handle->ego_entry->keystring, received_cid))
2118 break;
2119 }
2120 if (NULL == handle->ego_entry)
2121 {
2122 GNUNET_free (received_cpw);
2123 GNUNET_free (received_cid);
2124 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2125 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2126 return GNUNET_SYSERR;
2127 }
2128 GNUNET_STRINGS_string_to_data (received_cid,
2129 strlen (received_cid),
2130 cid,
2131 sizeof(struct GNUNET_IDENTITY_PublicKey));
2132
2133 GNUNET_free (received_cpw);
2134 GNUNET_free (received_cid);
2135 return GNUNET_OK;
2136}
2137
2138
2139const struct EgoEntry *
2140find_ego (struct RequestHandle *handle,
2141 struct GNUNET_IDENTITY_PublicKey *test_key)
2142{
2143 struct EgoEntry *ego_entry;
2144 struct GNUNET_IDENTITY_PublicKey pub_key;
2145
2146 for (ego_entry = ego_head; NULL != ego_entry;
2147 ego_entry = ego_entry->next)
2148 {
2149 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
2150 if (0 == GNUNET_memcmp (&pub_key, test_key))
2151 return ego_entry;
2152 }
2153 return NULL;
2154}
2155
2156
2157/**
2158 * Responds to token url-encoded POST request
2159 *
2160 * @param con_handle the connection handle
2161 * @param url the url
2162 * @param cls the RequestHandle
2163 */
2164static void
2165token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2166 const char *url,
2167 void *cls)
2168{
2169 struct RequestHandle *handle = cls;
2170 const struct EgoEntry *ego_entry = NULL;
2171 struct GNUNET_TIME_Relative expiration_time;
2172 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2173 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2174 struct GNUNET_RECLAIM_Ticket ticket;
2175 struct GNUNET_IDENTITY_PublicKey cid;
2176 struct GNUNET_HashCode cache_key;
2177 struct MHD_Response *resp = NULL;
2178 char *grant_type = NULL;
2179 char *code = NULL;
2180 char *json_response = NULL;
2181 char *id_token = NULL;
2182 char *access_token = NULL;
2183 char *jwa = NULL;
2184 char *jwt_secret = NULL;
2185 char *nonce = NULL;
2186 char *code_verifier = NULL;
2187 json_t *oidc_jwk = NULL;
2188 char *oidc_jwk_path = NULL;
2189 char *oidc_directory = NULL;
2190 char *tmp_at = NULL;
2191
2192 /*
2193 * Check Authorization
2194 */
2195 if (GNUNET_SYSERR == check_authorization (handle, &cid))
2196 {
2197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2198 "OIDC authorization for token endpoint failed\n");
2199 GNUNET_SCHEDULER_add_now (&do_error, handle);
2200 return;
2201 }
2202
2203 /*
2204 * Check parameter
2205 */
2206
2207 // TODO Do not allow multiple equal parameter names
2208 // REQUIRED grant_type
2209 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
2210 strlen (OIDC_GRANT_TYPE_KEY),
2211 &cache_key);
2212 grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
2213 if (NULL == grant_type)
2214 {
2215 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2216 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
2217 handle->response_code = MHD_HTTP_BAD_REQUEST;
2218 GNUNET_SCHEDULER_add_now (&do_error, handle);
2219 return;
2220 }
2221
2222 // Check parameter grant_type == "authorization_code"
2223 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
2224 {
2225 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
2226 handle->response_code = MHD_HTTP_BAD_REQUEST;
2227 GNUNET_free (grant_type);
2228 GNUNET_SCHEDULER_add_now (&do_error, handle);
2229 return;
2230 }
2231 GNUNET_free (grant_type);
2232 // REQUIRED code
2233 code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
2234 if (NULL == code)
2235 {
2236 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2237 handle->edesc = GNUNET_strdup ("missing parameter code");
2238 handle->response_code = MHD_HTTP_BAD_REQUEST;
2239 GNUNET_SCHEDULER_add_now (&do_error, handle);
2240 return;
2241 }
2242 ego_entry = find_ego (handle, &cid);
2243 if (NULL == ego_entry)
2244 {
2245 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2246 handle->edesc = GNUNET_strdup ("Unknown client");
2247 handle->response_code = MHD_HTTP_BAD_REQUEST;
2248 GNUNET_free (code);
2249 GNUNET_SCHEDULER_add_now (&do_error, handle);
2250 return;
2251 }
2252
2253 // REQUIRED code verifier
2254 code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2255 if (NULL == code_verifier)
2256 {
2257 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2258 "OAuth authorization request does not contain PKCE parameters!\n");
2259
2260 }
2261
2262 // decode code
2263 if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket,
2264 &cl, &pl, &nonce,
2265 OIDC_VERIFICATION_DEFAULT))
2266 {
2267 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2268 handle->edesc = GNUNET_strdup ("invalid code");
2269 handle->response_code = MHD_HTTP_BAD_REQUEST;
2270 GNUNET_free (code);
2271 if (NULL != code_verifier)
2272 GNUNET_free (code_verifier);
2273 GNUNET_SCHEDULER_add_now (&do_error, handle);
2274 return;
2275 }
2276 if (NULL != code_verifier)
2277 GNUNET_free (code_verifier);
2278
2279 // create jwt
2280 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
2281 "reclaim-rest-plugin",
2282 "expiration_time",
2283 &expiration_time))
2284 {
2285 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2286 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2287 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2288 GNUNET_free (code);
2289 if (NULL != nonce)
2290 GNUNET_free (nonce);
2291 GNUNET_RECLAIM_attribute_list_destroy (cl);
2292 GNUNET_RECLAIM_presentation_list_destroy (pl);
2293 GNUNET_SCHEDULER_add_now (&do_error, handle);
2294 return;
2295 }
2296
2297 // Check if HMAC or RSA should be used
2298 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2299 "reclaim-rest-plugin",
2300 "oidc_json_web_algorithm",
2301 &jwa))
2302 {
2303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2304 "Could not read OIDC JSON Web Algorithm config attribute."
2305 "Defaulting to RS256.");
2306 jwa = JWT_ALG_VALUE_RSA;
2307 }
2308
2309 if ( ! strcmp (jwa, JWT_ALG_VALUE_RSA))
2310 {
2311 // Replace for now
2312 oidc_jwk_path = get_oidc_jwk_path (cls);
2313 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2314
2315 // Check if secret JWK exists
2316 if (! oidc_jwk)
2317 {
2318 // Generate and save a new key
2319 oidc_jwk = generate_jwk ();
2320 oidc_directory = get_oidc_dir_path (cls);
2321
2322 // Create new oidc directory
2323 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2324 {
2325 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2326 ("Failed to create directory `%s' for storing oidc data\n"),
2327 oidc_directory);
2328 }
2329 else
2330 {
2331 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2332 }
2333 }
2334
2335 // Generate oidc token
2336 id_token = OIDC_generate_id_token_rsa (&ticket.audience,
2337 &ticket.identity,
2338 cl,
2339 pl,
2340 &expiration_time,
2341 (NULL != nonce) ? nonce : NULL,
2342 oidc_jwk);
2343 }
2344 else if ( ! strcmp (jwa, JWT_ALG_VALUE_HMAC))
2345 {
2346 // TODO OPTIONAL acr,amr,azp
2347 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2348 "reclaim-rest-plugin",
2349 "jwt_secret",
2350 &jwt_secret))
2351 {
2352 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2353 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2354 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2355 GNUNET_free (code);
2356 GNUNET_RECLAIM_attribute_list_destroy (cl);
2357 GNUNET_RECLAIM_presentation_list_destroy (pl);
2358 if (NULL != nonce)
2359 GNUNET_free (nonce);
2360 GNUNET_SCHEDULER_add_now (&do_error, handle);
2361 return;
2362 }
2363
2364 id_token = OIDC_generate_id_token_hmac (&ticket.audience,
2365 &ticket.identity,
2366 cl,
2367 pl,
2368 &expiration_time,
2369 (NULL != nonce) ? nonce : NULL,
2370 jwt_secret);
2371
2372 GNUNET_free (jwt_secret);
2373 }
2374 else
2375 {
2376 // TODO: OPTION NOT FOUND ERROR
2377 }
2378
2379 if (NULL != nonce)
2380 GNUNET_free (nonce);
2381 access_token = OIDC_access_token_new (&ticket);
2382 /**
2383 * Store mapping from access token to code so we can later
2384 * fall back on the provided attributes in userinfo one time.
2385 */
2386 GNUNET_CRYPTO_hash (access_token,
2387 strlen (access_token),
2388 &cache_key);
2389 /**
2390 * Note to future self: This cache has the following purpose:
2391 * Some OIDC plugins call the userendpoint right after receiving an
2392 * ID token and access token. There are reasons why this would make sense.
2393 * Others not so much.
2394 * In any case, in order to smoothen out the user experience upon login
2395 * (authorization), we speculatively cache the next
2396 * userinfo response in case the actual resolution through reclaim/GNS
2397 * takes too long.
2398 */
2399 tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2400 &cache_key);
2401 GNUNET_CONTAINER_multihashmap_put (oidc_code_cache,
2402 &cache_key,
2403 code,
2404 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
2405 /* If there was a previous code in there, free the old value */
2406 if (NULL != tmp_at)
2407 {
2408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2409 "OIDC access token already issued. Cleanup.\n");
2410 GNUNET_free (tmp_at);
2411 }
2412
2413 OIDC_build_token_response (access_token,
2414 id_token,
2415 &expiration_time,
2416 &json_response);
2417
2418 resp = GNUNET_REST_create_response (json_response);
2419 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2420 "Cache-Control",
2421 "no-store"));
2422 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2423 "Pragma", "no-cache"));
2424 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2425 "Content-Type",
2426 "application/json"));
2427 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2428 GNUNET_RECLAIM_attribute_list_destroy (cl);
2429 GNUNET_RECLAIM_presentation_list_destroy (pl);
2430 GNUNET_free (access_token);
2431 GNUNET_free (json_response);
2432 GNUNET_free (id_token);
2433 cleanup_handle (handle);
2434}
2435
2436
2437/**
2438 * Collects claims and stores them in handle
2439 */
2440static void
2441consume_ticket (void *cls,
2442 const struct GNUNET_IDENTITY_PublicKey *identity,
2443 const struct GNUNET_RECLAIM_Attribute *attr,
2444 const struct GNUNET_RECLAIM_Presentation *presentation)
2445{
2446 struct RequestHandle *handle = cls;
2447 struct GNUNET_RECLAIM_AttributeListEntry *ale;
2448 struct GNUNET_RECLAIM_PresentationListEntry *atle;
2449 struct MHD_Response *resp;
2450 struct GNUNET_HashCode cache_key;
2451 char *result_str;
2452 char *cached_code;
2453
2454 if (NULL != handle->consume_timeout_op)
2455 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
2456 handle->consume_timeout_op = NULL;
2457 handle->idp_op = NULL;
2458
2459 /**
2460 * We received a reply. In any case clear the cache.
2461 */
2462 GNUNET_CRYPTO_hash (handle->access_token,
2463 strlen (handle->access_token),
2464 &cache_key);
2465 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2466 &cache_key);
2467 if (NULL != cached_code)
2468 {
2469 GNUNET_assert (GNUNET_YES ==
2470 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2471 &cache_key,
2472 cached_code));
2473 GNUNET_free (cached_code);
2474 }
2475
2476
2477 if (NULL == identity)
2478 {
2479 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2480 handle->attr_userinfo_list,
2481 handle->presentations);
2482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2483 resp = GNUNET_REST_create_response (result_str);
2484 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2485 GNUNET_free (result_str);
2486 cleanup_handle (handle);
2487 return;
2488 }
2489 ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
2490 ale->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
2491 &attr->credential,
2492 attr->type,
2493 attr->data,
2494 attr->data_size);
2495 ale->attribute->id = attr->id;
2496 ale->attribute->flag = attr->flag;
2497 ale->attribute->credential = attr->credential;
2498 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
2499 handle->attr_userinfo_list->list_tail,
2500 ale);
2501 if (NULL == presentation)
2502 return;
2503 for (atle = handle->presentations->list_head;
2504 NULL != atle; atle = atle->next)
2505 {
2506 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (
2507 &atle->presentation->credential_id,
2508 &presentation->credential_id))
2509 continue;
2510 break; /** already in list **/
2511 }
2512 if (NULL == atle)
2513 {
2514 /** Credential matches for attribute, add **/
2515 atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry);
2516 atle->presentation = GNUNET_RECLAIM_presentation_new (presentation->type,
2517 presentation->data,
2518 presentation->
2519 data_size);
2520 atle->presentation->credential_id = presentation->credential_id;
2521 GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head,
2522 handle->presentations->list_tail,
2523 atle);
2524 }
2525}
2526
2527
2528static void
2529consume_fail (void *cls)
2530{
2531 struct RequestHandle *handle = cls;
2532 struct GNUNET_HashCode cache_key;
2533 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2534 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2535 struct GNUNET_RECLAIM_Ticket ticket;
2536 struct MHD_Response *resp;
2537 char *nonce;
2538 char *cached_code;
2539 char *result_str;
2540
2541
2542 handle->consume_timeout_op = NULL;
2543 if (NULL != handle->idp_op)
2544 GNUNET_RECLAIM_cancel (handle->idp_op);
2545 handle->idp_op = NULL;
2546
2547 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2548 "Ticket consumptioned timed out. Using cache...\n");
2549 GNUNET_CRYPTO_hash (handle->access_token,
2550 strlen (handle->access_token),
2551 &cache_key);
2552 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2553 &cache_key);
2554 if (NULL == cached_code)
2555 {
2556 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2557 handle->edesc = GNUNET_strdup ("No Access Token in cache!");
2558 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2559 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2560 return;
2561 }
2562 /**
2563 * Remove the cached item
2564 */
2565 GNUNET_assert (GNUNET_YES ==
2566 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2567 &cache_key,
2568 cached_code));
2569
2570 // decode code
2571 if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience,
2572 cached_code, NULL, &ticket,
2573 &cl, &pl, &nonce,
2574 OIDC_VERIFICATION_NO_CODE_VERIFIER))
2575 {
2576 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2577 handle->edesc = GNUNET_strdup ("invalid code");
2578 handle->response_code = MHD_HTTP_BAD_REQUEST;
2579 GNUNET_free (cached_code);
2580 if (NULL != nonce)
2581 GNUNET_free (nonce);
2582 GNUNET_SCHEDULER_add_now (&do_error, handle);
2583 return;
2584 }
2585
2586 GNUNET_free (cached_code);
2587
2588 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2589 cl,
2590 pl);
2591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2592 resp = GNUNET_REST_create_response (result_str);
2593 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2594 GNUNET_free (result_str);
2595 GNUNET_free (nonce);
2596 GNUNET_RECLAIM_attribute_list_destroy (cl);
2597 GNUNET_RECLAIM_presentation_list_destroy (pl);
2598 cleanup_handle (handle);
2599}
2600
2601
2602/**
2603 * Responds to userinfo GET and url-encoded POST request
2604 *
2605 * @param con_handle the connection handle
2606 * @param url the url
2607 * @param cls the RequestHandle
2608 */
2609static void
2610userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2611 const char *url,
2612 void *cls)
2613{
2614 // TODO expiration time
2615 struct RequestHandle *handle = cls;
2616 struct GNUNET_RECLAIM_Ticket *ticket;
2617 char delimiter[] = " ";
2618 struct GNUNET_HashCode cache_key;
2619 char *authorization;
2620 char *authorization_type;
2621 char *authorization_access_token;
2622 const struct EgoEntry *aud_ego;
2623 const struct GNUNET_IDENTITY_PrivateKey *privkey;
2624
2625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting userinfo\n");
2626 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2627 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2628 &cache_key);
2629 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2630 ->header_param_map,
2631 &cache_key))
2632 {
2633 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2634 handle->edesc = GNUNET_strdup ("No Access Token");
2635 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2636 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2637 return;
2638 }
2639 authorization =
2640 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2641 &cache_key);
2642
2643 // split header in "Bearer" and access_token
2644 authorization = GNUNET_strdup (authorization);
2645 authorization_type = strtok (authorization, delimiter);
2646 if ((NULL == authorization_type) ||
2647 (0 != strcmp ("Bearer", authorization_type)))
2648 {
2649 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2650 handle->edesc = GNUNET_strdup ("No Access Token");
2651 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2652 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2653 GNUNET_free (authorization);
2654 return;
2655 }
2656 authorization_access_token = strtok (NULL, delimiter);
2657 if (NULL == authorization_access_token)
2658 {
2659 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2660 handle->edesc = GNUNET_strdup ("Access token missing");
2661 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2662 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2663 GNUNET_free (authorization);
2664 return;
2665 }
2666
2667 if (GNUNET_OK != OIDC_access_token_parse (authorization_access_token,
2668 &ticket))
2669 {
2670 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2671 handle->edesc = GNUNET_strdup ("The access token is invalid");
2672 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2673 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2674 GNUNET_free (authorization);
2675 return;
2676
2677 }
2678 GNUNET_assert (NULL != ticket);
2679 handle->ticket = *ticket;
2680 GNUNET_free (ticket);
2681 aud_ego = find_ego (handle, &handle->ticket.audience);
2682 if (NULL == aud_ego)
2683 {
2684 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2685 handle->edesc = GNUNET_strdup ("The access token expired");
2686 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2687 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2688 GNUNET_free (authorization);
2689 return;
2690 }
2691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Consuming ticket\n");
2692 privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego);
2693 handle->attr_userinfo_list =
2694 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
2695 handle->presentations =
2696 GNUNET_new (struct GNUNET_RECLAIM_PresentationList);
2697
2698 /* If the consume takes too long, we use values from the cache */
2699 handle->access_token = GNUNET_strdup (authorization_access_token);
2700 handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (consume_timeout,
2701 &consume_fail,
2702 handle);
2703 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
2704 privkey,
2705 &handle->ticket,
2706 &consume_ticket,
2707 handle);
2708 GNUNET_free (authorization);
2709}
2710
2711/**
2712 * Responds to /jwks.json
2713 *
2714 * @param con_handle the connection handle
2715 * @param url the url
2716 * @param cls the RequestHandle
2717 */
2718static void
2719jwks_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2720 const char *url,
2721 void *cls)
2722{
2723 char *oidc_directory;
2724 char *oidc_jwk_path;
2725 char *oidc_jwk_pub_str;
2726 json_t *oidc_jwk;
2727 struct MHD_Response *resp;
2728 struct RequestHandle *handle = cls;
2729
2730 oidc_jwk_path = get_oidc_jwk_path (cls);
2731 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2732
2733 // Check if secret JWK exists
2734 if (! oidc_jwk)
2735 {
2736 // Generate and save a new key
2737 oidc_jwk = generate_jwk ();
2738 oidc_directory = get_oidc_dir_path (cls);
2739
2740 // Create new oidc directory
2741 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2742 {
2743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2744 ("Failed to create directory `%s' for storing oidc data\n"),
2745 oidc_directory);
2746 }
2747 else
2748 {
2749 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2750 }
2751 }
2752
2753 // Convert secret JWK to public JWK
2754 jose_jwk_pub (NULL, oidc_jwk);
2755
2756 // Encode JWK as string and return to API endpoint
2757 oidc_jwk_pub_str = json_dumps (oidc_jwk, JSON_INDENT (1));
2758 resp = GNUNET_REST_create_response (oidc_jwk_pub_str);
2759 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2760 json_decref (oidc_jwk);
2761 GNUNET_free (oidc_jwk_pub_str);
2762 GNUNET_free (oidc_jwk_pub_str);
2763 cleanup_handle (handle);
2764}
2765
2766/**
2767 * If listing is enabled, prints information about the egos.
2768 *
2769 * This function is initially called for all egos and then again
2770 * whenever a ego's identifier changes or if it is deleted. At the
2771 * end of the initial pass over all egos, the function is once called
2772 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2773 * be invoked in the future or that there was an error.
2774 *
2775 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
2776 * function is only called ONCE, and 'NULL' being passed in 'ego' does
2777 * indicate an error (for example because name is taken or no default value is
2778 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
2779 * value WILL be passed to a subsequent call to the identity callback of
2780 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
2781 *
2782 * When an identity is renamed, this function is called with the
2783 * (known) ego but the NEW identifier.
2784 *
2785 * When an identity is deleted, this function is called with the
2786 * (known) ego and "NULL" for the 'identifier'. In this case,
2787 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2788 * cleaned up).
2789 *
2790 * @param cls closure
2791 * @param ego ego handle
2792 * @param ctx context for application to store data for this ego
2793 * (during the lifetime of this process, initially NULL)
2794 * @param identifier identifier assigned by the user for this ego,
2795 * NULL if the user just deleted the ego and it
2796 * must thus no longer be used
2797 */
2798static void
2799list_ego (void *cls,
2800 struct GNUNET_IDENTITY_Ego *ego,
2801 void **ctx,
2802 const char *identifier)
2803{
2804 struct EgoEntry *ego_entry;
2805 struct GNUNET_IDENTITY_PublicKey pk;
2806
2807 if (NULL == ego)
2808 {
2809 state = ID_REST_STATE_POST_INIT;
2810 return;
2811 }
2812 if (ID_REST_STATE_INIT == state)
2813
2814 {
2815 ego_entry = GNUNET_new (struct EgoEntry);
2816 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2817 ego_entry->keystring = GNUNET_IDENTITY_public_key_to_string (&pk);
2818 ego_entry->ego = ego;
2819 ego_entry->identifier = GNUNET_strdup (identifier);
2820 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2821 ego_tail,
2822 ego_entry);
2823 return;
2824 }
2825 /* Ego renamed or added */
2826 if (identifier != NULL)
2827 {
2828 for (ego_entry = ego_head; NULL != ego_entry;
2829 ego_entry = ego_entry->next)
2830 {
2831 if (ego_entry->ego == ego)
2832 {
2833 /* Rename */
2834 GNUNET_free (ego_entry->identifier);
2835 ego_entry->identifier = GNUNET_strdup (identifier);
2836 break;
2837 }
2838 }
2839 if (NULL == ego_entry)
2840 {
2841 /* Add */
2842 ego_entry = GNUNET_new (struct EgoEntry);
2843 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2844 ego_entry->keystring = GNUNET_IDENTITY_public_key_to_string (&pk);
2845 ego_entry->ego = ego;
2846 ego_entry->identifier = GNUNET_strdup (identifier);
2847 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2848 ego_tail,
2849 ego_entry);
2850 }
2851 }
2852 else
2853 {
2854 /* Delete */
2855 for (ego_entry = ego_head; NULL != ego_entry;
2856 ego_entry = ego_entry->next)
2857 {
2858 if (ego_entry->ego == ego)
2859 break;
2860 }
2861 if (NULL == ego_entry)
2862 return; /* Not found */
2863
2864 GNUNET_CONTAINER_DLL_remove (ego_head,
2865 ego_tail,
2866 ego_entry);
2867 GNUNET_free (ego_entry->identifier);
2868 GNUNET_free (ego_entry->keystring);
2869 GNUNET_free (ego_entry);
2870 return;
2871 }
2872}
2873
2874
2875static void
2876oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2877 const char *url,
2878 void *cls)
2879{
2880 json_t *oidc_config;
2881 json_t *auth_methods;
2882 json_t *sig_algs;
2883 json_t *scopes;
2884 json_t *response_types;
2885 json_t *sub_types;
2886 json_t *claim_types;
2887 char *oidc_config_str;
2888 struct MHD_Response *resp;
2889 struct RequestHandle *handle = cls;
2890
2891 oidc_config = json_object ();
2892 // FIXME get from config?
2893 json_object_set_new (oidc_config,
2894 "issuer", json_string ("http://localhost:7776"));
2895 json_object_set_new (oidc_config,
2896 "authorization_endpoint",
2897 json_string ("https://api.reclaim/openid/authorize"));
2898 json_object_set_new (oidc_config,
2899 "token_endpoint",
2900 json_string ("http://localhost:7776/openid/token"));
2901 auth_methods = json_array ();
2902 json_array_append_new (auth_methods,
2903 json_string ("client_secret_basic"));
2904 json_array_append_new (auth_methods,
2905 json_string ("client_secret_post"));
2906 json_object_set_new (oidc_config,
2907 "token_endpoint_auth_methods_supported",
2908 auth_methods);
2909 sig_algs = json_array ();
2910 json_array_append_new (sig_algs,
2911 json_string ("HS512"));
2912 json_array_append_new (sig_algs,
2913 json_string ("RS256"));
2914 json_object_set_new (oidc_config,
2915 "id_token_signing_alg_values_supported",
2916 sig_algs);
2917 json_object_set_new (oidc_config,
2918 "jwks_uri",
2919 json_string ("http://localhost:7776/jwks.json"));
2920 json_object_set_new (oidc_config,
2921 "userinfo_endpoint",
2922 json_string ("http://localhost:7776/openid/userinfo"));
2923 scopes = json_array ();
2924 json_array_append_new (scopes,
2925 json_string ("openid"));
2926 json_array_append_new (scopes,
2927 json_string ("profile"));
2928 json_array_append_new (scopes,
2929 json_string ("email"));
2930 json_array_append_new (scopes,
2931 json_string ("address"));
2932 json_array_append_new (scopes,
2933 json_string ("phone"));
2934 json_object_set_new (oidc_config,
2935 "scopes_supported",
2936 scopes);
2937 response_types = json_array ();
2938 json_array_append_new (response_types,
2939 json_string ("code"));
2940 json_object_set_new (oidc_config,
2941 "response_types_supported",
2942 response_types);
2943 sub_types = json_array ();
2944 json_array_append_new (sub_types,
2945 json_string ("public")); /* no pairwise support */
2946 json_object_set_new (oidc_config,
2947 "subject_types_supported",
2948 sub_types);
2949 claim_types = json_array ();
2950 json_array_append_new (claim_types,
2951 json_string ("normal"));
2952 json_array_append_new (claim_types,
2953 json_string ("aggregated"));
2954 json_object_set_new (oidc_config,
2955 "claim_types_supported",
2956 claim_types);
2957 json_object_set_new (oidc_config,
2958 "claims_parameter_supported",
2959 json_boolean (1));
2960 oidc_config_str = json_dumps (oidc_config, JSON_INDENT (1));
2961 resp = GNUNET_REST_create_response (oidc_config_str);
2962 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2963 json_decref (oidc_config);
2964 GNUNET_free (oidc_config_str);
2965 cleanup_handle (handle);
2966}
2967
2968
2969/**
2970 * Respond to OPTIONS request
2971 *
2972 * @param con_handle the connection handle
2973 * @param url the url
2974 * @param cls the RequestHandle
2975 */
2976static void
2977oidc_config_cors (struct GNUNET_REST_RequestHandle *con_handle,
2978 const char *url,
2979 void *cls)
2980{
2981 struct MHD_Response *resp;
2982 struct RequestHandle *handle = cls;
2983
2984 // For now, independent of path return all options
2985 resp = GNUNET_REST_create_response (NULL);
2986 GNUNET_assert (MHD_NO !=
2987 MHD_add_response_header (resp,
2988 "Access-Control-Allow-Methods",
2989 allow_methods));
2990 GNUNET_assert (MHD_NO !=
2991 MHD_add_response_header (resp,
2992 "Access-Control-Allow-Origin",
2993 "*"));
2994 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2995 cleanup_handle (handle);
2996 return;
2997}
2998
2999
3000static enum GNUNET_GenericReturnValue
3001rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
3002 GNUNET_REST_ResultProcessor proc,
3003 void *proc_cls)
3004{
3005 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
3006 struct GNUNET_REST_RequestHandlerError err;
3007 static const struct GNUNET_REST_RequestHandler handlers[] =
3008 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
3009 { MHD_HTTP_METHOD_POST,
3010 GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint }, // url-encoded
3011 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
3012 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
3013 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3014 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3015 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_JWKS, &jwks_endpoint },
3016 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
3017 &oidc_config_endpoint },
3018 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,
3019 &oidc_config_cors },
3020 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
3021 GNUNET_REST_HANDLER_END };
3022
3023 handle->oidc = GNUNET_new (struct OIDC_Variables);
3024 if (NULL == OIDC_cookie_jar_map)
3025 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
3026 GNUNET_NO);
3027 if (NULL == oidc_code_cache)
3028 oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10,
3029 GNUNET_NO);
3030
3031 handle->response_code = 0;
3032 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
3033 handle->proc_cls = proc_cls;
3034 handle->proc = proc;
3035 handle->rest_handle = rest_handle;
3036 handle->url = GNUNET_strdup (rest_handle->url);
3037 handle->timeout_task =
3038 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
3039 GNUNET_CONTAINER_DLL_insert (requests_head,
3040 requests_tail,
3041 handle);
3042 if (handle->url[strlen (handle->url) - 1] == '/')
3043 handle->url[strlen (handle->url) - 1] = '\0';
3044 if (GNUNET_NO ==
3045 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
3046 return GNUNET_NO;
3047
3048 return GNUNET_YES;
3049}
3050
3051
3052/**
3053 * Entry point for the plugin.
3054 *
3055 * @param cls Config info
3056 * @return NULL on error, otherwise the plugin context
3057 */
3058void *
3059libgnunet_plugin_rest_openid_connect_init (void *cls)
3060{
3061 static struct Plugin plugin;
3062 struct GNUNET_REST_Plugin *api;
3063
3064 cfg = cls;
3065 if (NULL != plugin.cfg)
3066 return NULL; /* can only initialize once! */
3067 memset (&plugin, 0, sizeof(struct Plugin));
3068 plugin.cfg = cfg;
3069 api = GNUNET_new (struct GNUNET_REST_Plugin);
3070 api->cls = &plugin;
3071 api->name = GNUNET_REST_API_NS_OIDC;
3072 api->process_request = &rest_identity_process_request;
3073 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
3074 gns_handle = GNUNET_GNS_connect (cfg);
3075 idp = GNUNET_RECLAIM_connect (cfg);
3076 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
3077 "reclaim-rest-plugin",
3078 "OIDC_USERINFO_CONSUME_TIMEOUT",
3079 &consume_timeout))
3080 {
3081 consume_timeout = CONSUME_TIMEOUT;
3082 }
3083
3084
3085 state = ID_REST_STATE_INIT;
3086 GNUNET_asprintf (&allow_methods,
3087 "%s, %s, %s, %s, %s",
3088 MHD_HTTP_METHOD_GET,
3089 MHD_HTTP_METHOD_POST,
3090 MHD_HTTP_METHOD_PUT,
3091 MHD_HTTP_METHOD_DELETE,
3092 MHD_HTTP_METHOD_OPTIONS);
3093
3094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3095 _ ("OpenID Connect REST API initialized\n"));
3096 return api;
3097}
3098
3099
3100static int
3101cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value)
3102{
3103 GNUNET_free (value);
3104 return GNUNET_YES;
3105}
3106
3107
3108/**
3109 * Exit point from the plugin.
3110 *
3111 * @param cls the plugin context (as returned by "init")
3112 * @return always NULL
3113 */
3114void *
3115libgnunet_plugin_rest_openid_connect_done (void *cls)
3116{
3117 struct GNUNET_REST_Plugin *api = cls;
3118 struct Plugin *plugin = api->cls;
3119 struct EgoEntry *ego_entry;
3120
3121 plugin->cfg = NULL;
3122 while (NULL != requests_head)
3123 cleanup_handle (requests_head);
3124 if (NULL != OIDC_cookie_jar_map)
3125 {
3126 GNUNET_CONTAINER_multihashmap_iterate (OIDC_cookie_jar_map,
3127 &cleanup_hashmap,
3128 NULL);
3129 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
3130 }
3131 if (NULL != oidc_code_cache)
3132 {
3133 GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache,
3134 &cleanup_hashmap,
3135 NULL);
3136 GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache);
3137 }
3138
3139 GNUNET_free (allow_methods);
3140 if (NULL != gns_handle)
3141 GNUNET_GNS_disconnect (gns_handle);
3142 if (NULL != identity_handle)
3143 GNUNET_IDENTITY_disconnect (identity_handle);
3144 if (NULL != idp)
3145 GNUNET_RECLAIM_disconnect (idp);
3146 while (NULL != (ego_entry = ego_head))
3147 {
3148 GNUNET_CONTAINER_DLL_remove (ego_head,
3149 ego_tail,
3150 ego_entry);
3151 GNUNET_free (ego_entry->identifier);
3152 GNUNET_free (ego_entry->keystring);
3153 GNUNET_free (ego_entry);
3154 }
3155 GNUNET_free (api);
3156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3157 "OpenID Connect REST plugin is finished\n");
3158 return NULL;
3159}
3160
3161
3162/* end of plugin_rest_openid_connect.c */