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