aboutsummaryrefslogtreecommitdiff
path: root/src/reclaim
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim')
-rw-r--r--src/reclaim/Makefile.am44
-rw-r--r--src/reclaim/json_reclaim.c242
-rw-r--r--src/reclaim/json_reclaim.h48
-rw-r--r--src/reclaim/oidc_helper.c436
-rw-r--r--src/reclaim/oidc_helper.h111
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c2330
-rw-r--r--src/reclaim/plugin_rest_reclaim.c1104
7 files changed, 4314 insertions, 1 deletions
diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am
index f9942fa23..fd488e1b9 100644
--- a/src/reclaim/Makefile.am
+++ b/src/reclaim/Makefile.am
@@ -16,6 +16,12 @@ if HAVE_SQLITE
16SQLITE_PLUGIN = libgnunet_plugin_reclaim_sqlite.la 16SQLITE_PLUGIN = libgnunet_plugin_reclaim_sqlite.la
17endif 17endif
18 18
19if HAVE_JSON
20REST_PLUGIN = \
21 libgnunet_plugin_rest_openid_connect.la \
22 libgnunet_plugin_rest_reclaim.la
23endif
24
19EXTRA_DIST = \ 25EXTRA_DIST = \
20 reclaim.conf \ 26 reclaim.conf \
21 test_reclaim_defaults.conf \ 27 test_reclaim_defaults.conf \
@@ -33,7 +39,8 @@ lib_LTLIBRARIES = \
33 libgnunetreclaim.la 39 libgnunetreclaim.la
34plugin_LTLIBRARIES = \ 40plugin_LTLIBRARIES = \
35 libgnunet_plugin_gnsrecord_reclaim.la \ 41 libgnunet_plugin_gnsrecord_reclaim.la \
36 $(SQLITE_PLUGIN) 42 $(SQLITE_PLUGIN) \
43 $(REST_PLUGIN)
37 44
38bin_PROGRAMS = \ 45bin_PROGRAMS = \
39 gnunet-reclaim 46 gnunet-reclaim
@@ -41,6 +48,41 @@ bin_PROGRAMS = \
41libexec_PROGRAMS = \ 48libexec_PROGRAMS = \
42 gnunet-service-reclaim 49 gnunet-service-reclaim
43 50
51libgnunet_plugin_rest_reclaim_la_SOURCES = \
52 plugin_rest_reclaim.c \
53 json_reclaim.h \
54 json_reclaim.c
55libgnunet_plugin_rest_reclaim_la_LIBADD = \
56 $(top_builddir)/src/identity/libgnunetidentity.la \
57 libgnunetreclaim.la \
58 $(top_builddir)/src/json/libgnunetjson.la \
59 $(top_builddir)/src/rest/libgnunetrest.la \
60 $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \
61 $(top_builddir)/src/namestore/libgnunetnamestore.la \
62 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
63 $(LTLIBINTL) -ljansson -lmicrohttpd
64libgnunet_plugin_rest_reclaim_la_LDFLAGS = \
65 $(GN_PLUGIN_LDFLAGS)
66
67
68libgnunet_plugin_rest_openid_connect_la_SOURCES = \
69 plugin_rest_openid_connect.c \
70 oidc_helper.h \
71 oidc_helper.c
72libgnunet_plugin_rest_openid_connect_la_LIBADD = \
73 $(top_builddir)/src/identity/libgnunetidentity.la \
74 libgnunetreclaim.la \
75 $(top_builddir)/src/rest/libgnunetrest.la \
76 $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \
77 $(top_builddir)/src/namestore/libgnunetnamestore.la \
78$(top_builddir)/src/gns/libgnunetgns.la \
79 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
80 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
81 $(LTLIBINTL) -ljansson -lmicrohttpd
82libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \
83 $(GN_PLUGIN_LDFLAGS)
84
85
44libgnunet_plugin_gnsrecord_reclaim_la_SOURCES = \ 86libgnunet_plugin_gnsrecord_reclaim_la_SOURCES = \
45 plugin_gnsrecord_reclaim.c 87 plugin_gnsrecord_reclaim.c
46libgnunet_plugin_gnsrecord_reclaim_la_LIBADD = \ 88libgnunet_plugin_gnsrecord_reclaim_la_LIBADD = \
diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c
new file mode 100644
index 000000000..0fe9150d9
--- /dev/null
+++ b/src/reclaim/json_reclaim.c
@@ -0,0 +1,242 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/**
22 * @file rest-plugins/json_reclaim.c
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_service.h"
30#include "gnunet_reclaim_attribute_lib.h"
31
32/**
33 * Parse given JSON object to a claim
34 *
35 * @param cls closure, NULL
36 * @param root the json object representing data
37 * @param spec where to write the data
38 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
39 */
40static int
41parse_attr (void *cls,
42 json_t *root,
43 struct GNUNET_JSON_Specification *spec)
44{
45 struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr;
46 const char* name_str;
47 const char* val_str;
48 const char* type_str;
49 char *data;
50 int unpack_state;
51 uint32_t type;
52 size_t data_size;
53
54 GNUNET_assert(NULL != root);
55
56 if(!json_is_object(root))
57 {
58 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
59 "Error json is not array nor object!\n");
60 return GNUNET_SYSERR;
61 }
62 //interpret single attribute
63 unpack_state = json_unpack(root,
64 "{s:s, s:s, s:s!}",
65 "name", &name_str,
66 "type", &type_str,
67 "value", &val_str);
68 if (0 != unpack_state)
69 {
70 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
71 "Error json object has a wrong format!\n");
72 return GNUNET_SYSERR;
73 }
74 type = GNUNET_RECLAIM_ATTRIBUTE_typename_to_number (type_str);
75 if (GNUNET_SYSERR == (GNUNET_RECLAIM_ATTRIBUTE_string_to_value (type,
76 val_str,
77 (void**)&data,
78 &data_size)))
79 {
80 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
81 "Attribute value invalid!\n");
82 return GNUNET_SYSERR;
83 }
84 attr = GNUNET_RECLAIM_ATTRIBUTE_claim_new (name_str,
85 type,
86 data,
87 data_size);
88 *(struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr = attr;
89 return GNUNET_OK;
90}
91
92/**
93 * Cleanup data left from parsing RSA public key.
94 *
95 * @param cls closure, NULL
96 * @param[out] spec where to free the data
97 */
98static void
99clean_attr (void *cls, struct GNUNET_JSON_Specification *spec)
100{
101 struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr;
102 attr = (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr;
103 if (NULL != *attr)
104 {
105 GNUNET_free(*attr);
106 *attr = NULL;
107 }
108}
109
110/**
111 * JSON Specification for Reclaim claims.
112 *
113 * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill
114 * @return JSON Specification
115 */
116struct GNUNET_JSON_Specification
117GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr)
118{
119 struct GNUNET_JSON_Specification ret = {
120 .parser = &parse_attr,
121 .cleaner = &clean_attr,
122 .cls = NULL,
123 .field = NULL,
124 .ptr = attr,
125 .ptr_size = 0,
126 .size_ptr = NULL
127 };
128 *attr = NULL;
129 return ret;
130}
131/**
132 * Parse given JSON object to a ticket
133 *
134 * @param cls closure, NULL
135 * @param root the json object representing data
136 * @param spec where to write the data
137 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
138 */
139static int
140parse_ticket (void *cls,
141 json_t *root,
142 struct GNUNET_JSON_Specification *spec)
143{
144 struct GNUNET_RECLAIM_Ticket *ticket;
145 const char* rnd_str;
146 const char* aud_str;
147 const char* id_str;
148 int unpack_state;
149
150 GNUNET_assert(NULL != root);
151
152 if(!json_is_object(root))
153 {
154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
155 "Error json is not array nor object!\n");
156 return GNUNET_SYSERR;
157 }
158 //interpret single ticket
159 unpack_state = json_unpack(root,
160 "{s:s, s:s, s:s!}",
161 "rnd", &rnd_str,
162 "audience", &aud_str,
163 "identity", &id_str);
164 if (0 != unpack_state)
165 {
166 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
167 "Error json object has a wrong format!\n");
168 return GNUNET_SYSERR;
169 }
170 ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
171 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (rnd_str,
172 strlen (rnd_str),
173 &ticket->rnd,
174 sizeof (uint64_t)))
175 {
176 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Rnd invalid\n");
177 GNUNET_free(ticket);
178 return GNUNET_SYSERR;
179 }
180 GNUNET_STRINGS_string_to_data (id_str,
181 strlen (id_str),
182 &ticket->identity,
183 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
184 {
185 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Identity invalid\n");
186 GNUNET_free(ticket);
187 return GNUNET_SYSERR;
188 }
189
190 GNUNET_STRINGS_string_to_data (aud_str,
191 strlen (aud_str),
192 &ticket->audience,
193 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
194 {
195 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Audience invalid\n");
196 GNUNET_free(ticket);
197 return GNUNET_SYSERR;
198 }
199
200 *(struct GNUNET_RECLAIM_Ticket **) spec->ptr = ticket;
201 return GNUNET_OK;
202}
203
204/**
205 * Cleanup data left from parsing RSA public key.
206 *
207 * @param cls closure, NULL
208 * @param[out] spec where to free the data
209 */
210static void
211clean_ticket (void *cls, struct GNUNET_JSON_Specification *spec)
212{
213 struct GNUNET_RECLAIM_Ticket **ticket;
214 ticket = (struct GNUNET_RECLAIM_Ticket **) spec->ptr;
215 if (NULL != *ticket)
216 {
217 GNUNET_free(*ticket);
218 *ticket = NULL;
219 }
220}
221
222/**
223 * JSON Specification for Reclaim tickets.
224 *
225 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
226 * @return JSON Specification
227 */
228struct GNUNET_JSON_Specification
229GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket)
230{
231 struct GNUNET_JSON_Specification ret = {
232 .parser = &parse_ticket,
233 .cleaner = &clean_ticket,
234 .cls = NULL,
235 .field = NULL,
236 .ptr = ticket,
237 .ptr_size = 0,
238 .size_ptr = NULL
239 };
240 *ticket = NULL;
241 return ret;
242}
diff --git a/src/reclaim/json_reclaim.h b/src/reclaim/json_reclaim.h
new file mode 100644
index 000000000..ced2e10dd
--- /dev/null
+++ b/src/reclaim/json_reclaim.h
@@ -0,0 +1,48 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/**
22 * @file rest-plugins/json_reclaim.h
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_service.h"
30#include "gnunet_reclaim_attribute_lib.h"
31
32/**
33 * JSON Specification for Reclaim claims.
34 *
35 * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill
36 * @return JSON Specification
37 */
38struct GNUNET_JSON_Specification
39GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr);
40
41/**
42 * JSON Specification for Reclaim tickets.
43 *
44 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
45 * @return JSON Specification
46 */
47struct GNUNET_JSON_Specification
48GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket);
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c
new file mode 100644
index 000000000..646e58551
--- /dev/null
+++ b/src/reclaim/oidc_helper.c
@@ -0,0 +1,436 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2015 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/**
22 * @file reclaim/oidc_helper.c
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_signatures.h"
29#include "gnunet_reclaim_service.h"
30#include "gnunet_reclaim_attribute_lib.h"
31#include <jansson.h>
32#include <inttypes.h>
33#include "oidc_helper.h"
34
35static char*
36create_jwt_header(void)
37{
38 json_t *root;
39 char *json_str;
40
41 root = json_object ();
42 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
43 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
44
45 json_str = json_dumps (root, JSON_INDENT(0) | JSON_COMPACT);
46 json_decref (root);
47 return json_str;
48}
49
50static void
51replace_char(char* str, char find, char replace){
52 char *current_pos = strchr(str,find);
53 while (current_pos){
54 *current_pos = replace;
55 current_pos = strchr(current_pos,find);
56 }
57}
58
59//RFC4648
60static void
61fix_base64(char* str) {
62 //Replace + with -
63 replace_char (str, '+', '-');
64
65 //Replace / with _
66 replace_char (str, '/', '_');
67
68}
69
70/**
71 * Create a JWT from attributes
72 *
73 * @param aud_key the public of the audience
74 * @param sub_key the public key of the subject
75 * @param attrs the attribute list
76 * @param expiration_time the validity of the token
77 * @param secret_key the key used to sign the JWT
78 * @return a new base64-encoded JWT string.
79 */
80char*
81OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
82 const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key,
83 const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
84 const struct GNUNET_TIME_Relative *expiration_time,
85 const char *nonce,
86 const char *secret_key)
87{
88 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
89 struct GNUNET_HashCode signature;
90 struct GNUNET_TIME_Absolute exp_time;
91 struct GNUNET_TIME_Absolute time_now;
92 char* audience;
93 char* subject;
94 char* header;
95 char* body_str;
96 char* result;
97 char* header_base64;
98 char* body_base64;
99 char* signature_target;
100 char* signature_base64;
101 char* attr_val_str;
102 json_t* body;
103
104 //iat REQUIRED time now
105 time_now = GNUNET_TIME_absolute_get();
106 //exp REQUIRED time expired from config
107 exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
108 //auth_time only if max_age
109 //nonce only if nonce
110 // OPTIONAL acr,amr,azp
111 subject = GNUNET_STRINGS_data_to_string_alloc (sub_key,
112 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
113 audience = GNUNET_STRINGS_data_to_string_alloc (aud_key,
114 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
115 header = create_jwt_header ();
116 body = json_object ();
117
118 //iss REQUIRED case sensitive server uri with https
119 //The issuer is the local reclaim instance (e.g. https://reclaim.id/api/openid)
120 json_object_set_new (body,
121 "iss", json_string (SERVER_ADDRESS));
122 //sub REQUIRED public key identity, not exceed 255 ASCII length
123 json_object_set_new (body,
124 "sub", json_string (subject));
125 //aud REQUIRED public key client_id must be there
126 json_object_set_new (body,
127 "aud", json_string (audience));
128 //iat
129 json_object_set_new (body,
130 "iat", json_integer (time_now.abs_value_us / (1000*1000)));
131 //exp
132 json_object_set_new (body,
133 "exp", json_integer (exp_time.abs_value_us / (1000*1000)));
134 //nbf
135 json_object_set_new (body,
136 "nbf", json_integer (time_now.abs_value_us / (1000*1000)));
137 //nonce
138 if (NULL != nonce)
139 json_object_set_new (body,
140 "nonce", json_string (nonce));
141
142 for (le = attrs->list_head; NULL != le; le = le->next)
143 {
144 attr_val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type,
145 le->claim->data,
146 le->claim->data_size);
147 json_object_set_new (body,
148 le->claim->name,
149 json_string (attr_val_str));
150 GNUNET_free (attr_val_str);
151 }
152 body_str = json_dumps (body, JSON_INDENT(0) | JSON_COMPACT);
153 json_decref (body);
154
155 GNUNET_STRINGS_base64_encode (header,
156 strlen (header),
157 &header_base64);
158 fix_base64(header_base64);
159
160 GNUNET_STRINGS_base64_encode (body_str,
161 strlen (body_str),
162 &body_base64);
163 fix_base64(body_base64);
164
165 GNUNET_free (subject);
166 GNUNET_free (audience);
167
168 /**
169 * Creating the JWT signature. This might not be
170 * standards compliant, check.
171 */
172 GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
173 GNUNET_CRYPTO_hmac_raw (secret_key, strlen (secret_key), signature_target, strlen (signature_target), &signature);
174 GNUNET_STRINGS_base64_encode ((const char*)&signature,
175 sizeof (struct GNUNET_HashCode),
176 &signature_base64);
177 fix_base64(signature_base64);
178
179 GNUNET_asprintf (&result, "%s.%s.%s",
180 header_base64, body_base64, signature_base64);
181
182 GNUNET_free (signature_target);
183 GNUNET_free (header);
184 GNUNET_free (body_str);
185 GNUNET_free (signature_base64);
186 GNUNET_free (body_base64);
187 GNUNET_free (header_base64);
188 return result;
189}
190/**
191 * Builds an OIDC authorization code including
192 * a reclaim ticket and nonce
193 *
194 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
195 * @param ticket the ticket to include in the code
196 * @param nonce the nonce to include in the code
197 * @return a new authorization code (caller must free)
198 */
199char*
200OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
201 const struct GNUNET_RECLAIM_Ticket *ticket,
202 const char* nonce)
203{
204 char *ticket_str;
205 json_t *code_json;
206 char *signature_payload;
207 char *signature_str;
208 char *authz_code;
209 size_t signature_payload_len;
210 struct GNUNET_CRYPTO_EcdsaSignature signature;
211 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
212
213 signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket);
214 if (NULL != nonce)
215 signature_payload_len += strlen (nonce);
216
217 signature_payload = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len);
218 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *)signature_payload;
219 purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len);
220 purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
221 memcpy (&purpose[1],
222 ticket,
223 sizeof (struct GNUNET_RECLAIM_Ticket));
224 if (NULL != nonce)
225 memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket),
226 nonce,
227 strlen (nonce));
228 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_sign (issuer,
229 purpose,
230 &signature))
231 {
232 GNUNET_free (signature_payload);
233 return NULL;
234 }
235 signature_str = GNUNET_STRINGS_data_to_string_alloc (&signature,
236 sizeof (signature));
237 ticket_str = GNUNET_STRINGS_data_to_string_alloc (ticket,
238 sizeof (struct GNUNET_RECLAIM_Ticket));
239
240 code_json = json_object ();
241 json_object_set_new (code_json,
242 "ticket",
243 json_string (ticket_str));
244 if (NULL != nonce)
245 json_object_set_new (code_json,
246 "nonce",
247 json_string (nonce));
248 json_object_set_new (code_json,
249 "signature",
250 json_string (signature_str));
251 authz_code = json_dumps (code_json,
252 JSON_INDENT(0) | JSON_COMPACT);
253 GNUNET_free (signature_payload);
254 GNUNET_free (signature_str);
255 GNUNET_free (ticket_str);
256 json_decref (code_json);
257 return authz_code;
258}
259
260
261
262
263/**
264 * Parse reclaim ticket and nonce from
265 * authorization code.
266 * This also verifies the signature in the code.
267 *
268 * @param audience the expected audience of the code
269 * @param code the string representation of the code
270 * @param ticket where to store the ticket
271 * @param nonce where to store the nonce
272 * @return GNUNET_OK if successful, else GNUNET_SYSERR
273 */
274int
275OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
276 const char* code,
277 struct GNUNET_RECLAIM_Ticket **ticket,
278 char **nonce)
279{
280 json_error_t error;
281 json_t *code_json;
282 json_t *ticket_json;
283 json_t *nonce_json;
284 json_t *signature_json;
285 const char *ticket_str;
286 const char *signature_str;
287 const char *nonce_str;
288 char *code_output;
289 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
290 struct GNUNET_CRYPTO_EcdsaSignature signature;
291 size_t signature_payload_len;
292
293 code_output = NULL;
294 GNUNET_STRINGS_base64_decode (code,
295 strlen(code),
296 (void**)&code_output);
297 code_json = json_loads (code_output, 0 , &error);
298 GNUNET_free (code_output);
299 ticket_json = json_object_get (code_json, "ticket");
300 nonce_json = json_object_get (code_json, "nonce");
301 signature_json = json_object_get (code_json, "signature");
302 *ticket = NULL;
303 *nonce = NULL;
304
305 if ((NULL == ticket_json || !json_is_string (ticket_json)) ||
306 (NULL == signature_json || !json_is_string (signature_json)))
307 {
308 json_decref (code_json);
309 return GNUNET_SYSERR;
310 }
311 ticket_str = json_string_value (ticket_json);
312 signature_str = json_string_value (signature_json);
313 nonce_str = NULL;
314 if (NULL != nonce_json)
315 nonce_str = json_string_value (nonce_json);
316 signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket);
317 if (NULL != nonce_str)
318 signature_payload_len += strlen (nonce_str);
319 purpose = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
320 signature_payload_len);
321 purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len);
322 purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
323 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (ticket_str,
324 strlen (ticket_str),
325 &purpose[1],
326 sizeof (struct GNUNET_RECLAIM_Ticket)))
327 {
328 GNUNET_free (purpose);
329 json_decref (code_json);
330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
331 "Cannot parse ticket!\n");
332 return GNUNET_SYSERR;
333 }
334 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (signature_str,
335 strlen (signature_str),
336 &signature,
337 sizeof (struct GNUNET_CRYPTO_EcdsaSignature)))
338 {
339 GNUNET_free (purpose);
340 json_decref (code_json);
341 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
342 "Cannot parse signature!\n");
343 return GNUNET_SYSERR;
344 }
345 *ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
346 memcpy (*ticket,
347 &purpose[1],
348 sizeof (struct GNUNET_RECLAIM_Ticket));
349 if (0 != memcmp (audience,
350 &(*ticket)->audience,
351 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
352 {
353 GNUNET_free (purpose);
354 GNUNET_free (*ticket);
355 json_decref (code_json);
356 *ticket = NULL;
357 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358 "Audience in ticket does not match client!\n");
359 return GNUNET_SYSERR;
360
361 }
362 if (NULL != nonce_str)
363 memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket),
364 nonce_str,
365 strlen (nonce_str));
366 if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
367 purpose,
368 &signature,
369 &(*ticket)->identity))
370 {
371 GNUNET_free (purpose);
372 GNUNET_free (*ticket);
373 json_decref (code_json);
374 *ticket = NULL;
375 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376 "Signature of authZ code invalid!\n");
377 return GNUNET_SYSERR;
378 }
379 *nonce = GNUNET_strdup (nonce_str);
380 return GNUNET_OK;
381}
382
383/**
384 * Build a token response for a token request
385 * TODO: Maybe we should add the scope here?
386 *
387 * @param access_token the access token to include
388 * @param id_token the id_token to include
389 * @param expiration_time the expiration time of the token(s)
390 * @param token_response where to store the response
391 */
392void
393OIDC_build_token_response (const char *access_token,
394 const char *id_token,
395 const struct GNUNET_TIME_Relative *expiration_time,
396 char **token_response)
397{
398 json_t *root_json;
399
400 root_json = json_object ();
401
402 GNUNET_assert (NULL != access_token);
403 GNUNET_assert (NULL != id_token);
404 GNUNET_assert (NULL != expiration_time);
405 json_object_set_new (root_json,
406 "access_token",
407 json_string (access_token));
408 json_object_set_new (root_json,
409 "token_type",
410 json_string ("Bearer"));
411 json_object_set_new (root_json,
412 "expires_in",
413 json_integer (expiration_time->rel_value_us / (1000 * 1000)));
414 json_object_set_new (root_json,
415 "id_token",
416 json_string (id_token));
417 *token_response = json_dumps (root_json,
418 JSON_INDENT(0) | JSON_COMPACT);
419 json_decref (root_json);
420}
421
422/**
423 * Generate a new access token
424 */
425char*
426OIDC_access_token_new ()
427{
428 char* access_token_number;
429 char* access_token;
430 uint64_t random_number;
431
432 random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
433 GNUNET_asprintf (&access_token_number, "%" PRIu64, random_number);
434 GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token);
435 return access_token;
436}
diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h
new file mode 100644
index 000000000..d718b7a78
--- /dev/null
+++ b/src/reclaim/oidc_helper.h
@@ -0,0 +1,111 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2015 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/**
22 * @file reclaim/oidc_helper.h
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 */
26
27#ifndef JWT_H
28#define JWT_H
29
30#define JWT_ALG "alg"
31
32/* Use 512bit HMAC */
33#define JWT_ALG_VALUE "HS512"
34
35#define JWT_TYP "typ"
36
37#define JWT_TYP_VALUE "jwt"
38
39#define SERVER_ADDRESS "https://api.reclaim"
40
41/**
42 * Create a JWT from attributes
43 *
44 * @param aud_key the public of the audience
45 * @param sub_key the public key of the subject
46 * @param attrs the attribute list
47 * @param expiration_time the validity of the token
48 * @param secret_key the key used to sign the JWT
49 * @return a new base64-encoded JWT string.
50 */
51char*
52OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
53 const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key,
54 const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
55 const struct GNUNET_TIME_Relative *expiration_time,
56 const char *nonce,
57 const char *secret_key);
58
59/**
60 * Builds an OIDC authorization code including
61 * a reclaim ticket and nonce
62 *
63 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
64 * @param ticket the ticket to include in the code
65 * @param nonce the nonce to include in the code
66 * @return a new authorization code (caller must free)
67 */
68char*
69OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
70 const struct GNUNET_RECLAIM_Ticket *ticket,
71 const char* nonce);
72
73/**
74 * Parse reclaim ticket and nonce from
75 * authorization code.
76 * This also verifies the signature in the code.
77 *
78 * @param audience the expected audience of the code
79 * @param code the string representation of the code
80 * @param ticket where to store the ticket
81 * @param nonce where to store the nonce
82 * @return GNUNET_OK if successful, else GNUNET_SYSERR
83 */
84int
85OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
86 const char* code,
87 struct GNUNET_RECLAIM_Ticket **ticket,
88 char **nonce);
89
90/**
91 * Build a token response for a token request
92 * TODO: Maybe we should add the scope here?
93 *
94 * @param access_token the access token to include
95 * @param id_token the id_token to include
96 * @param expiration_time the expiration time of the token(s)
97 * @param token_response where to store the response
98 */
99void
100OIDC_build_token_response (const char *access_token,
101 const char *id_token,
102 const struct GNUNET_TIME_Relative *expiration_time,
103 char **token_response);
104/**
105 * Generate a new access token
106 */
107char*
108OIDC_access_token_new ();
109
110
111#endif
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
new file mode 100644
index 000000000..e755f079b
--- /dev/null
+++ b/src/reclaim/plugin_rest_openid_connect.c
@@ -0,0 +1,2330 @@
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 * @file identity/plugin_rest_openid_connect.c
24 * @brief GNUnet Namestore REST plugin
25 *
26 */
27
28#include "platform.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_identity_service.h"
31#include "gnunet_gns_service.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_namestore_service.h"
34#include "gnunet_rest_lib.h"
35#include "microhttpd.h"
36#include <jansson.h>
37#include <inttypes.h>
38#include "gnunet_signatures.h"
39#include "gnunet_reclaim_attribute_lib.h"
40#include "gnunet_reclaim_service.h"
41#include "oidc_helper.h"
42
43/**
44 * REST root namespace
45 */
46#define GNUNET_REST_API_NS_OIDC "/openid"
47
48/**
49 * Authorize endpoint
50 */
51#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
52
53/**
54 * Token endpoint
55 */
56#define GNUNET_REST_API_NS_TOKEN "/openid/token"
57
58/**
59 * UserInfo endpoint
60 */
61#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
62
63/**
64 * Login namespace
65 */
66#define GNUNET_REST_API_NS_LOGIN "/openid/login"
67
68/**
69 * State while collecting all egos
70 */
71#define ID_REST_STATE_INIT 0
72
73/**
74 * Done collecting egos
75 */
76#define ID_REST_STATE_POST_INIT 1
77
78/**
79 * OIDC grant_type key
80 */
81#define OIDC_GRANT_TYPE_KEY "grant_type"
82
83/**
84 * OIDC grant_type key
85 */
86#define OIDC_GRANT_TYPE_VALUE "authorization_code"
87
88/**
89 * OIDC code key
90 */
91#define OIDC_CODE_KEY "code"
92
93/**
94 * OIDC response_type key
95 */
96#define OIDC_RESPONSE_TYPE_KEY "response_type"
97
98/**
99 * OIDC client_id key
100 */
101#define OIDC_CLIENT_ID_KEY "client_id"
102
103/**
104 * OIDC scope key
105 */
106#define OIDC_SCOPE_KEY "scope"
107
108/**
109 * OIDC redirect_uri key
110 */
111#define OIDC_REDIRECT_URI_KEY "redirect_uri"
112
113/**
114 * OIDC state key
115 */
116#define OIDC_STATE_KEY "state"
117
118/**
119 * OIDC nonce key
120 */
121#define OIDC_NONCE_KEY "nonce"
122
123/**
124 * OIDC cookie expiration (in seconds)
125 */
126#define OIDC_COOKIE_EXPIRATION 3
127
128/**
129 * OIDC cookie header key
130 */
131#define OIDC_COOKIE_HEADER_KEY "cookie"
132
133/**
134 * OIDC cookie header information key
135 */
136#define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
137
138/**
139 * OIDC cookie header information key
140 */
141#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
142
143/**
144 * OIDC cookie header if user cancelled
145 */
146#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
147
148/**
149 * OIDC expected response_type while authorizing
150 */
151#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
152
153/**
154 * OIDC expected scope part while authorizing
155 */
156#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
157
158/**
159 * OIDC error key for invalid client
160 */
161#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
162
163/**
164 * OIDC error key for invalid scopes
165 */
166#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
167
168/**
169 * OIDC error key for invalid requests
170 */
171#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
172
173/**
174 * OIDC error key for invalid tokens
175 */
176#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
177
178/**
179 * OIDC error key for invalid cookies
180 */
181#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
182
183/**
184 * OIDC error key for generic server errors
185 */
186#define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
187
188/**
189 * OIDC error key for unsupported grants
190 */
191#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
192
193/**
194 * OIDC error key for unsupported response types
195 */
196#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
197
198/**
199 * OIDC error key for unauthorized clients
200 */
201#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
202
203/**
204 * OIDC error key for denied access
205 */
206#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
207
208
209
210/**
211 * OIDC ignored parameter array
212 */
213static char* OIDC_ignored_parameter_array [] =
214{
215 "display",
216 "prompt",
217 "ui_locales",
218 "response_mode",
219 "id_token_hint",
220 "login_hint",
221 "acr_values"
222};
223
224/**
225 * OIDC Hash map that keeps track of issued cookies
226 */
227struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
228
229/**
230 * OIDC authorized identities and times hashmap
231 */
232struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
233
234/**
235 * OIDC Hash map that keeps track of used authorization code(s)
236 */
237struct GNUNET_CONTAINER_MultiHashMap *OIDC_used_ticket_map;
238
239/**
240 * Hash map that links the issued access token to the corresponding ticket and ego
241 */
242struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map;
243
244/**
245 * The configuration handle
246 */
247const struct GNUNET_CONFIGURATION_Handle *cfg;
248
249/**
250 * HTTP methods allows for this plugin
251 */
252static char* allow_methods;
253
254/**
255 * @brief struct returned by the initialization function of the plugin
256 */
257struct Plugin
258{
259 const struct GNUNET_CONFIGURATION_Handle *cfg;
260};
261
262/**
263 * OIDC needed variables
264 */
265struct OIDC_Variables
266{
267 /**
268 * The RP client public key
269 */
270 struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
271
272 /**
273 * The OIDC client id of the RP
274 */
275 char *client_id;
276
277 /**
278 * The OIDC redirect uri
279 */
280 char *redirect_uri;
281
282 /**
283 * The list of oidc scopes
284 */
285 char *scope;
286
287 /**
288 * The OIDC state
289 */
290 char *state;
291
292 /**
293 * The OIDC nonce
294 */
295 char *nonce;
296
297 /**
298 * The OIDC response type
299 */
300 char *response_type;
301
302 /**
303 * The identity chosen by the user to login
304 */
305 char *login_identity;
306
307 /**
308 * User cancelled authorization/login
309 */
310 int user_cancelled;
311
312 /**
313 * The response JSON
314 */
315 json_t *response;
316
317};
318
319/**
320 * The ego list
321 */
322struct EgoEntry
323{
324 /**
325 * DLL
326 */
327 struct EgoEntry *next;
328
329 /**
330 * DLL
331 */
332 struct EgoEntry *prev;
333
334 /**
335 * Ego Identifier
336 */
337 char *identifier;
338
339 /**
340 * Public key string
341 */
342 char *keystring;
343
344 /**
345 * The Ego
346 */
347 struct GNUNET_IDENTITY_Ego *ego;
348};
349
350
351struct RequestHandle
352{
353 /**
354 * Ego list
355 */
356 struct EgoEntry *ego_head;
357
358 /**
359 * Ego list
360 */
361 struct EgoEntry *ego_tail;
362
363 /**
364 * Selected ego
365 */
366 struct EgoEntry *ego_entry;
367
368 /**
369 * Pointer to ego private key
370 */
371 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
372
373 /**
374 * OIDC variables
375 */
376 struct OIDC_Variables *oidc;
377
378 /**
379 * The processing state
380 */
381 int state;
382
383 /**
384 * Handle to Identity service.
385 */
386 struct GNUNET_IDENTITY_Handle *identity_handle;
387
388 /**
389 * Rest connection
390 */
391 struct GNUNET_REST_RequestHandle *rest_handle;
392
393 /**
394 * GNS handle
395 */
396 struct GNUNET_GNS_Handle *gns_handle;
397
398 /**
399 * GNS lookup op
400 */
401 struct GNUNET_GNS_LookupRequest *gns_op;
402
403 /**
404 * Handle to NAMESTORE
405 */
406 struct GNUNET_NAMESTORE_Handle *namestore_handle;
407
408 /**
409 * Iterator for NAMESTORE
410 */
411 struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
412
413 /**
414 * Attribute claim list
415 */
416 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
417
418 /**
419 * IDENTITY Operation
420 */
421 struct GNUNET_IDENTITY_Operation *op;
422
423 /**
424 * Identity Provider
425 */
426 struct GNUNET_RECLAIM_Handle *idp;
427
428 /**
429 * Idp Operation
430 */
431 struct GNUNET_RECLAIM_Operation *idp_op;
432
433 /**
434 * Attribute iterator
435 */
436 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
437
438 /**
439 * Ticket iterator
440 */
441 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
442
443 /**
444 * A ticket
445 */
446 struct GNUNET_RECLAIM_Ticket ticket;
447
448 /**
449 * Desired timeout for the lookup (default is no timeout).
450 */
451 struct GNUNET_TIME_Relative timeout;
452
453 /**
454 * ID of a task associated with the resolution process.
455 */
456 struct GNUNET_SCHEDULER_Task *timeout_task;
457
458 /**
459 * The plugin result processor
460 */
461 GNUNET_REST_ResultProcessor proc;
462
463 /**
464 * The closure of the result processor
465 */
466 void *proc_cls;
467
468 /**
469 * The url
470 */
471 char *url;
472
473 /**
474 * The tld for redirect
475 */
476 char *tld;
477
478 /**
479 * The redirect prefix
480 */
481 char *redirect_prefix;
482
483 /**
484 * The redirect suffix
485 */
486 char *redirect_suffix;
487
488 /**
489 * Error response message
490 */
491 char *emsg;
492
493 /**
494 * Error response description
495 */
496 char *edesc;
497
498 /**
499 * Reponse code
500 */
501 int response_code;
502
503};
504
505/**
506 * Cleanup lookup handle
507 * @param handle Handle to clean up
508 */
509static void
510cleanup_handle (struct RequestHandle *handle)
511{
512 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
513 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
514 struct EgoEntry *ego_entry;
515 struct EgoEntry *ego_tmp;
516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517 "Cleaning up\n");
518 if (NULL != handle->timeout_task)
519 GNUNET_SCHEDULER_cancel (handle->timeout_task);
520 if (NULL != handle->identity_handle)
521 GNUNET_IDENTITY_disconnect (handle->identity_handle);
522 if (NULL != handle->attr_it)
523 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
524 if (NULL != handle->ticket_it)
525 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
526 if (NULL != handle->idp)
527 GNUNET_RECLAIM_disconnect (handle->idp);
528 GNUNET_free_non_null (handle->url);
529 GNUNET_free_non_null (handle->tld);
530 GNUNET_free_non_null (handle->redirect_prefix);
531 GNUNET_free_non_null (handle->redirect_suffix);
532 GNUNET_free_non_null (handle->emsg);
533 GNUNET_free_non_null (handle->edesc);
534 if (NULL != handle->gns_op)
535 GNUNET_GNS_lookup_cancel (handle->gns_op);
536 if (NULL != handle->gns_handle)
537 GNUNET_GNS_disconnect (handle->gns_handle);
538
539 if (NULL != handle->namestore_handle)
540 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
541 if (NULL != handle->oidc)
542 {
543 GNUNET_free_non_null (handle->oidc->client_id);
544 GNUNET_free_non_null (handle->oidc->login_identity);
545 GNUNET_free_non_null (handle->oidc->nonce);
546 GNUNET_free_non_null (handle->oidc->redirect_uri);
547 GNUNET_free_non_null (handle->oidc->response_type);
548 GNUNET_free_non_null (handle->oidc->scope);
549 GNUNET_free_non_null (handle->oidc->state);
550 json_decref (handle->oidc->response);
551 GNUNET_free (handle->oidc);
552 }
553 if ( NULL != handle->attr_list )
554 {
555 for (claim_entry = handle->attr_list->list_head;
556 NULL != claim_entry;)
557 {
558 claim_tmp = claim_entry;
559 claim_entry = claim_entry->next;
560 GNUNET_free (claim_tmp->claim);
561 GNUNET_free (claim_tmp);
562 }
563 GNUNET_free (handle->attr_list);
564 }
565 for (ego_entry = handle->ego_head;
566 NULL != ego_entry;)
567 {
568 ego_tmp = ego_entry;
569 ego_entry = ego_entry->next;
570 GNUNET_free (ego_tmp->identifier);
571 GNUNET_free (ego_tmp->keystring);
572 GNUNET_free (ego_tmp);
573 }
574 GNUNET_free_non_null (handle->attr_it);
575 GNUNET_free (handle);
576}
577
578static void
579cleanup_handle_delayed (void *cls)
580{
581 cleanup_handle (cls);
582}
583
584
585/**
586 * Task run on error, sends error message. Cleans up everything.
587 *
588 * @param cls the `struct RequestHandle`
589 */
590static void
591do_error (void *cls)
592{
593 struct RequestHandle *handle = cls;
594 struct MHD_Response *resp;
595 char *json_error;
596
597 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
598 handle->emsg,
599 (NULL != handle->edesc) ? handle->edesc : "",
600 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
601 (NULL != handle->oidc->state) ? handle->oidc->state : "",
602 (NULL != handle->oidc->state) ? "\"" : "");
603 if ( 0 == handle->response_code )
604 handle->response_code = MHD_HTTP_BAD_REQUEST;
605 resp = GNUNET_REST_create_response (json_error);
606 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
607 MHD_add_response_header (resp,
608 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
609 "Basic");
610 MHD_add_response_header (resp,
611 MHD_HTTP_HEADER_CONTENT_TYPE,
612 "application/json");
613 handle->proc (handle->proc_cls, resp, handle->response_code);
614 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
615 GNUNET_free (json_error);
616}
617
618
619/**
620 * Task run on error in userinfo endpoint, sends error header. Cleans up
621 * everything
622 *
623 * @param cls the `struct RequestHandle`
624 */
625static void
626do_userinfo_error (void *cls)
627{
628 struct RequestHandle *handle = cls;
629 struct MHD_Response *resp;
630 char *error;
631
632 GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
633 handle->emsg,
634 (NULL != handle->edesc) ? handle->edesc : "");
635 resp = GNUNET_REST_create_response ("");
636 MHD_add_response_header (resp,
637 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
638 "Bearer");
639 handle->proc (handle->proc_cls, resp, handle->response_code);
640 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
641 GNUNET_free (error);
642}
643
644
645/**
646 * Task run on error, sends error message and redirects. Cleans up everything.
647 *
648 * @param cls the `struct RequestHandle`
649 */
650static void
651do_redirect_error (void *cls)
652{
653 struct RequestHandle *handle = cls;
654 struct MHD_Response *resp;
655 char* redirect;
656 GNUNET_asprintf (&redirect,
657 "%s?error=%s&error_description=%s%s%s",
658 handle->oidc->redirect_uri, handle->emsg, handle->edesc,
659 (NULL != handle->oidc->state) ? "&state=" : "",
660 (NULL != handle->oidc->state) ? handle->oidc->state : "");
661 resp = GNUNET_REST_create_response ("");
662 MHD_add_response_header (resp, "Location", redirect);
663 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
664 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
665 GNUNET_free (redirect);
666}
667
668/**
669 * Task run on timeout, sends error message. Cleans up everything.
670 *
671 * @param cls the `struct RequestHandle`
672 */
673static void
674do_timeout (void *cls)
675{
676 struct RequestHandle *handle = cls;
677
678 handle->timeout_task = NULL;
679 do_error (handle);
680}
681
682/**
683 * Return attributes for claim
684 *
685 * @param cls the request handle
686 */
687static void
688return_userinfo_response (void *cls)
689{
690 char* result_str;
691 struct RequestHandle *handle = cls;
692 struct MHD_Response *resp;
693
694 result_str = json_dumps (handle->oidc->response, 0);
695
696 resp = GNUNET_REST_create_response (result_str);
697 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
698 GNUNET_free (result_str);
699 cleanup_handle (handle);
700}
701
702/**
703 * Returns base64 encoded string urlencoded
704 *
705 * @param string the string to encode
706 * @return base64 encoded string
707 */
708static char*
709base64_encode (const char *s)
710{
711 char *enc;
712 char *enc_urlencode;
713 char *tmp;
714 int i;
715 int num_pads = 0;
716
717 GNUNET_STRINGS_base64_encode (s, strlen (s), &enc);
718 tmp = strchr (enc, '=');
719 num_pads = strlen (enc) - (tmp - enc);
720 GNUNET_assert ((3 > num_pads) && (0 <= num_pads));
721 if (0 == num_pads)
722 return enc;
723 enc_urlencode = GNUNET_malloc (strlen (enc) + num_pads*2);
724 strcpy (enc_urlencode, enc);
725 GNUNET_free (enc);
726 tmp = strchr (enc_urlencode, '=');
727 for (i = 0; i < num_pads; i++)
728 {
729 strcpy (tmp, "%3D"); //replace '=' with '%3D'
730 tmp += 3;
731 }
732 return enc_urlencode;
733}
734
735/**
736 * Respond to OPTIONS request
737 *
738 * @param con_handle the connection handle
739 * @param url the url
740 * @param cls the RequestHandle
741 */
742static void
743options_cont (struct GNUNET_REST_RequestHandle *con_handle,
744 const char* url,
745 void *cls)
746{
747 struct MHD_Response *resp;
748 struct RequestHandle *handle = cls;
749
750 //For now, independent of path return all options
751 resp = GNUNET_REST_create_response (NULL);
752 MHD_add_response_header (resp,
753 "Access-Control-Allow-Methods",
754 allow_methods);
755 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
756 cleanup_handle (handle);
757 return;
758}
759
760
761/**
762 * Interprets cookie header and pass its identity keystring to handle
763 */
764static void
765cookie_identity_interpretation (struct RequestHandle *handle)
766{
767 struct GNUNET_HashCode cache_key;
768 char *cookies;
769 struct GNUNET_TIME_Absolute current_time, *relog_time;
770 char delimiter[] = "; ";
771 char *tmp_cookies;
772 char *token;
773 char *value;
774
775 //gets identity of login try with cookie
776 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
777 &cache_key);
778 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
779 &cache_key) )
780 {
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "No cookie found\n");
783 return;
784 }
785 //splits cookies and find 'Identity' cookie
786 tmp_cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key);
787 cookies = GNUNET_strdup (tmp_cookies);
788 token = strtok (cookies, delimiter);
789 handle->oidc->user_cancelled = GNUNET_NO;
790 handle->oidc->login_identity = NULL;
791 if (NULL == token)
792 {
793 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
794 "Unable to parse cookie: %s\n",
795 cookies);
796 GNUNET_free (cookies);
797 return;
798 }
799
800 while (NULL != token)
801 {
802 if (0 == strcmp (token,
803 OIDC_COOKIE_HEADER_ACCESS_DENIED))
804 {
805 handle->oidc->user_cancelled = GNUNET_YES;
806 GNUNET_free (cookies);
807 return;
808 }
809 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
810 break;
811 token = strtok (NULL, delimiter);
812 }
813 if (NULL == token)
814 {
815 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
816 "No cookie value to process: %s\n",
817 cookies);
818 GNUNET_free (cookies);
819 return;
820 }
821 GNUNET_CRYPTO_hash (token, strlen (token),
822 &cache_key);
823 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
824 {
825 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
826 "Found cookie `%s', but no corresponding expiration entry present...\n",
827 token);
828 GNUNET_free (cookies);
829 return;
830 }
831 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map,
832 &cache_key);
833 current_time = GNUNET_TIME_absolute_get ();
834 // 30 min after old login -> redirect to login
835 if ( current_time.abs_value_us > relog_time->abs_value_us )
836 {
837 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
838 "Found cookie `%s', but it is expired.\n",
839 token);
840 GNUNET_free (cookies);
841 return;
842 }
843 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
844 GNUNET_assert (NULL != value);
845 handle->oidc->login_identity = GNUNET_strdup (value);
846}
847
848/**
849 * Redirects to login page stored in configuration file
850 */
851static void
852login_redirect (void *cls)
853{
854 char *login_base_url;
855 char *new_redirect;
856 struct MHD_Response *resp;
857 struct RequestHandle *handle = cls;
858
859 if ( GNUNET_OK
860 == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
861 "address", &login_base_url) )
862 {
863 GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
864 login_base_url,
865 OIDC_RESPONSE_TYPE_KEY,
866 handle->oidc->response_type,
867 OIDC_CLIENT_ID_KEY,
868 handle->oidc->client_id,
869 OIDC_REDIRECT_URI_KEY,
870 handle->oidc->redirect_uri,
871 OIDC_SCOPE_KEY,
872 handle->oidc->scope,
873 OIDC_STATE_KEY,
874 (NULL != handle->oidc->state) ? handle->oidc->state : "",
875 OIDC_NONCE_KEY,
876 (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
877 resp = GNUNET_REST_create_response ("");
878 MHD_add_response_header (resp, "Location", new_redirect);
879 GNUNET_free (login_base_url);
880 }
881 else
882 {
883 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
884 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
885 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
886 GNUNET_SCHEDULER_add_now (&do_error, handle);
887 return;
888 }
889 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
890 GNUNET_free (new_redirect);
891 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
892}
893
894/**
895 * Does internal server error when iteration failed.
896 */
897static void
898oidc_iteration_error (void *cls)
899{
900 struct RequestHandle *handle = cls;
901 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
902 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
903 GNUNET_SCHEDULER_add_now (&do_error, handle);
904}
905
906
907/**
908 * Issues ticket and redirects to relying party with the authorization code as
909 * parameter. Otherwise redirects with error
910 */
911static void
912oidc_ticket_issue_cb (void* cls,
913 const struct GNUNET_RECLAIM_Ticket *ticket)
914{
915 struct RequestHandle *handle = cls;
916 struct MHD_Response *resp;
917 char *ticket_str;
918 char *redirect_uri;
919 char *code_json_string;
920 char *code_base64_final_string;
921
922 handle->idp_op = NULL;
923 handle->ticket = *ticket;
924 if (NULL == ticket)
925 {
926 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
927 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
928 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
929 return;
930 }
931 ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
932 sizeof (struct GNUNET_RECLAIM_Ticket));
933 //TODO change if more attributes are needed (see max_age)
934 code_json_string = OIDC_build_authz_code (&handle->priv_key,
935 &handle->ticket,
936 handle->oidc->nonce);
937 code_base64_final_string = base64_encode (code_json_string);
938 if ( (NULL != handle->redirect_prefix) &&
939 (NULL != handle->redirect_suffix) &&
940 (NULL != handle->tld) )
941 {
942
943 GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s",
944 handle->redirect_prefix,
945 handle->tld,
946 handle->redirect_suffix,
947 handle->oidc->response_type,
948 code_base64_final_string, handle->oidc->state);
949 } else {
950 GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s",
951 handle->oidc->redirect_uri,
952 handle->oidc->response_type,
953 code_base64_final_string, handle->oidc->state);
954
955 }
956 resp = GNUNET_REST_create_response ("");
957 MHD_add_response_header (resp, "Location", redirect_uri);
958 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
959 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
960 GNUNET_free (redirect_uri);
961 GNUNET_free (ticket_str);
962 GNUNET_free (code_json_string);
963 GNUNET_free (code_base64_final_string);
964}
965
966static void
967oidc_collect_finished_cb (void *cls)
968{
969 struct RequestHandle *handle = cls;
970 handle->attr_it = NULL;
971 handle->ticket_it = NULL;
972 if (NULL == handle->attr_list->list_head)
973 {
974 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
975 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
976 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
977 return;
978 }
979 handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
980 &handle->priv_key,
981 &handle->oidc->client_pkey,
982 handle->attr_list,
983 &oidc_ticket_issue_cb,
984 handle);
985}
986
987
988/**
989 * Collects all attributes for an ego if in scope parameter
990 */
991static void
992oidc_attr_collect (void *cls,
993 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
994 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
995{
996 struct RequestHandle *handle = cls;
997 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
998 char* scope_variables;
999 char* scope_variable;
1000 char delimiter[]=" ";
1001
1002 if ( (NULL == attr->name) || (NULL == attr->data) )
1003 {
1004 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1005 return;
1006 }
1007
1008 scope_variables = GNUNET_strdup (handle->oidc->scope);
1009 scope_variable = strtok (scope_variables, delimiter);
1010 while (NULL != scope_variable)
1011 {
1012 if ( 0 == strcmp (attr->name, scope_variable) )
1013 {
1014 break;
1015 }
1016 scope_variable = strtok (NULL, delimiter);
1017 }
1018 if ( NULL == scope_variable )
1019 {
1020 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1021 GNUNET_free (scope_variables);
1022 return;
1023 }
1024 GNUNET_free (scope_variables);
1025
1026 le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1027 le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
1028 attr->data, attr->data_size);
1029 GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1030 handle->attr_list->list_tail, le);
1031 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1032}
1033
1034
1035/**
1036 * Checks time and cookie and redirects accordingly
1037 */
1038static void
1039code_redirect (void *cls)
1040{
1041 struct RequestHandle *handle = cls;
1042 struct GNUNET_TIME_Absolute current_time, *relog_time;
1043 struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
1044 struct GNUNET_HashCode cache_key;
1045 char *identity_cookie;
1046
1047 GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
1048 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1049 GNUNET_free (identity_cookie);
1050 //No login time for identity -> redirect to login
1051 if ( GNUNET_YES
1052 == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map,
1053 &cache_key) )
1054 {
1055 relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map,
1056 &cache_key);
1057 current_time = GNUNET_TIME_absolute_get ();
1058 // 30 min after old login -> redirect to login
1059 if ( current_time.abs_value_us <= relog_time->abs_value_us )
1060 {
1061 if ( GNUNET_OK
1062 != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1063 handle->oidc->login_identity,
1064 strlen (handle->oidc->login_identity), &pubkey) )
1065 {
1066 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1067 handle->edesc = GNUNET_strdup ("The cookie of a login identity is not valid");
1068 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1069 return;
1070 }
1071 // iterate over egos and compare their public key
1072 for (handle->ego_entry = handle->ego_head;
1073 NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1074 {
1075 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1076 if ( 0 == memcmp (&ego_pkey,
1077 &pubkey,
1078 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1079 {
1080 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1081 handle->idp = GNUNET_RECLAIM_connect (cfg);
1082 handle->attr_list = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1083 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp,
1084 &handle->priv_key,
1085 &oidc_iteration_error,
1086 handle,
1087 &oidc_attr_collect,
1088 handle,
1089 &oidc_collect_finished_cb,
1090 handle);
1091 return;
1092 }
1093 }
1094 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1095 return;
1096 }
1097 }
1098}
1099
1100
1101static void
1102build_redirect (void *cls)
1103{
1104 struct RequestHandle *handle = cls;
1105 struct MHD_Response *resp;
1106 char* redirect_uri;
1107
1108 if (GNUNET_YES == handle->oidc->user_cancelled)
1109 {
1110 if ( (NULL != handle->redirect_prefix) &&
1111 (NULL != handle->redirect_suffix) &&
1112 (NULL != handle->tld) )
1113 {
1114 GNUNET_asprintf (&redirect_uri, "%s.%s/%s?error=%s&error_description=%s&state=%s",
1115 handle->redirect_prefix,
1116 handle->tld,
1117 handle->redirect_suffix,
1118 "access_denied",
1119 "User denied access",
1120 handle->oidc->state);
1121 } else {
1122 GNUNET_asprintf (&redirect_uri, "%s?error=%s&error_description=%s&state=%s",
1123 handle->oidc->redirect_uri,
1124 "access_denied",
1125 "User denied access",
1126 handle->oidc->state);
1127
1128 }
1129 resp = GNUNET_REST_create_response ("");
1130 MHD_add_response_header (resp, "Location", redirect_uri);
1131 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1132 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1133 GNUNET_free (redirect_uri);
1134 return;
1135 }
1136 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1137}
1138
1139
1140static void
1141lookup_redirect_uri_result (void *cls,
1142 uint32_t rd_count,
1143 const struct GNUNET_GNSRECORD_Data *rd)
1144{
1145 struct RequestHandle *handle = cls;
1146 char *tmp;
1147 char *tmp_key_str;
1148 char *pos;
1149 struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1150
1151 handle->gns_op = NULL;
1152 if (0 == rd_count)
1153 {
1154 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1155 handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1156 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1157 return;
1158 }
1159 for (int i = 0; i < rd_count; i++)
1160 {
1161 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1162 continue;
1163 if (0 != strncmp (rd[i].data,
1164 handle->oidc->redirect_uri,
1165 rd[i].data_size))
1166 continue;
1167 tmp = GNUNET_strndup (rd[i].data,
1168 rd[i].data_size);
1169 if (NULL == strstr (tmp,
1170 handle->oidc->client_id))
1171 {
1172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1173 "Redirect uri %s does not contain client_id %s",
1174 tmp,
1175 handle->oidc->client_id);
1176 } else {
1177
1178 pos = strrchr (tmp,
1179 (unsigned char) '.');
1180 *pos = '\0';
1181 handle->redirect_prefix = GNUNET_strdup (tmp);
1182 tmp_key_str = pos + 1;
1183 pos = strchr (tmp_key_str,
1184 (unsigned char) '/');
1185 *pos = '\0';
1186 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1187
1188 GNUNET_STRINGS_string_to_data (tmp_key_str,
1189 strlen (tmp_key_str),
1190 &redirect_zone,
1191 sizeof (redirect_zone));
1192 }
1193 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1194 GNUNET_free (tmp);
1195 return;
1196 }
1197 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1198 handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1199 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1200}
1201
1202
1203
1204/**
1205 * Initiate redirect back to client.
1206 */
1207static void
1208client_redirect (void *cls)
1209{
1210 struct RequestHandle *handle = cls;
1211
1212 /* Lookup client redirect uri to verify request */
1213 handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle,
1214 GNUNET_GNS_EMPTY_LABEL_AT,
1215 &handle->oidc->client_pkey,
1216 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1217 GNUNET_GNS_LO_DEFAULT,
1218 &lookup_redirect_uri_result,
1219 handle);
1220
1221}
1222
1223
1224/**
1225 * Iteration over all results finished, build final
1226 * response.
1227 *
1228 * @param cls the `struct RequestHandle`
1229 */
1230static void
1231build_authz_response (void *cls)
1232{
1233 struct RequestHandle *handle = cls;
1234 struct GNUNET_HashCode cache_key;
1235
1236 char *expected_scope;
1237 char delimiter[]=" ";
1238 int number_of_ignored_parameter, iterator;
1239
1240
1241 // REQUIRED value: redirect_uri
1242 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1243 &cache_key);
1244 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1245 &cache_key))
1246 {
1247 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1248 handle->edesc=GNUNET_strdup ("missing parameter redirect_uri");
1249 GNUNET_SCHEDULER_add_now (&do_error, handle);
1250 return;
1251 }
1252 handle->oidc->redirect_uri =
1253 GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1254 &cache_key));
1255
1256 // REQUIRED value: response_type
1257 GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1258 &cache_key);
1259 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1260 &cache_key))
1261 {
1262 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1263 handle->edesc=GNUNET_strdup ("missing parameter response_type");
1264 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1265 return;
1266 }
1267 handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1268 &cache_key);
1269 handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1270
1271 // REQUIRED value: scope
1272 GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1273 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1274 &cache_key))
1275 {
1276 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1277 handle->edesc=GNUNET_strdup ("missing parameter scope");
1278 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1279 return;
1280 }
1281 handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1282 &cache_key);
1283 handle->oidc->scope = GNUNET_strdup (handle->oidc->scope);
1284
1285 //OPTIONAL value: nonce
1286 GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1287 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1288 &cache_key))
1289 {
1290 handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1291 &cache_key);
1292 handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1293 }
1294
1295 //TODO check other values if needed
1296 number_of_ignored_parameter = sizeof (OIDC_ignored_parameter_array) / sizeof (char *);
1297 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1298 {
1299 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1300 strlen (OIDC_ignored_parameter_array[iterator]),
1301 &cache_key);
1302 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1303 &cache_key))
1304 {
1305 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1306 GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1307 OIDC_ignored_parameter_array[iterator]);
1308 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1309 return;
1310 }
1311 }
1312
1313 // Checks if response_type is 'code'
1314 if (0 != strcmp (handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1315 {
1316 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1317 handle->edesc=GNUNET_strdup ("The authorization server does not support "
1318 "obtaining this authorization code.");
1319 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1320 return;
1321 }
1322
1323 // Checks if scope contains 'openid'
1324 expected_scope = GNUNET_strdup (handle->oidc->scope);
1325 char* test;
1326 test = strtok (expected_scope, delimiter);
1327 while (NULL != test)
1328 {
1329 if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1330 break;
1331 test = strtok (NULL, delimiter);
1332 }
1333 if (NULL == test)
1334 {
1335 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1336 handle->edesc=GNUNET_strdup ("The requested scope is invalid, unknown, or "
1337 "malformed.");
1338 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1339 GNUNET_free (expected_scope);
1340 return;
1341 }
1342
1343 GNUNET_free (expected_scope);
1344 if ( (NULL == handle->oidc->login_identity) &&
1345 (GNUNET_NO == handle->oidc->user_cancelled))
1346 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1347 else
1348 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1349}
1350
1351/**
1352 * Iterate over tlds in config
1353 */
1354static void
1355tld_iter (void *cls,
1356 const char *section,
1357 const char *option,
1358 const char *value)
1359{
1360 struct RequestHandle *handle = cls;
1361 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1362
1363 if (GNUNET_OK !=
1364 GNUNET_CRYPTO_ecdsa_public_key_from_string (value,
1365 strlen (value),
1366 &pkey))
1367 {
1368 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1369 "Skipping non key %s\n",
1370 value);
1371 return;
1372 }
1373 if (0 == memcmp (&pkey, &handle->oidc->client_pkey,
1374 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1375 handle->tld = GNUNET_strdup (option+1);
1376}
1377
1378/**
1379 * Responds to authorization GET and url-encoded POST request
1380 *
1381 * @param con_handle the connection handle
1382 * @param url the url
1383 * @param cls the RequestHandle
1384 */
1385static void
1386authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1387 const char* url,
1388 void *cls)
1389{
1390 struct RequestHandle *handle = cls;
1391 struct GNUNET_HashCode cache_key;
1392 struct EgoEntry *tmp_ego;
1393 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1394 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1395
1396 cookie_identity_interpretation (handle);
1397
1398 //RECOMMENDED value: state - REQUIRED for answers
1399 GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1400 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1401 &cache_key))
1402 {
1403 handle->oidc->state = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1404 &cache_key);
1405 handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1406 }
1407
1408 // REQUIRED value: client_id
1409 GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1410 &cache_key);
1411 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1412 &cache_key))
1413 {
1414 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1415 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1416 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1417 GNUNET_SCHEDULER_add_now (&do_error, handle);
1418 return;
1419 }
1420 handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1421 &cache_key));
1422
1423 if ( GNUNET_OK
1424 != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1425 strlen (handle->oidc->client_id),
1426 &handle->oidc->client_pkey) )
1427 {
1428 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1429 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1430 "authorization code using this method.");
1431 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1432 GNUNET_SCHEDULER_add_now (&do_error, handle);
1433 return;
1434 }
1435
1436
1437 if ( NULL == handle->ego_head )
1438 {
1439 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1440 handle->edesc = GNUNET_strdup ("Egos are missing");
1441 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1442 GNUNET_SCHEDULER_add_now (&do_error, handle);
1443 return;
1444 }
1445
1446 handle->ego_entry = handle->ego_head;
1447 handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1448 //If we know this identity, translated the corresponding TLD
1449 //TODO: We might want to have a reverse lookup functionality for TLDs?
1450 for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1451 {
1452 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1453 GNUNET_CRYPTO_ecdsa_key_get_public (priv_key,
1454 &pkey);
1455 if (0 == memcmp (&pkey, &handle->oidc->client_pkey,
1456 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1457 {
1458 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1459 handle->ego_entry = handle->ego_tail;
1460 }
1461 }
1462 if (NULL == handle->tld)
1463 GNUNET_CONFIGURATION_iterate_section_values (cfg,
1464 "gns",
1465 tld_iter,
1466 handle);
1467 if (NULL == handle->tld)
1468 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1469 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1470}
1471
1472/**
1473 * Combines an identity with a login time and responds OK to login request
1474 *
1475 * @param con_handle the connection handle
1476 * @param url the url
1477 * @param cls the RequestHandle
1478 */
1479static void
1480login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1481 const char* url,
1482 void *cls)
1483{
1484 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1485 struct RequestHandle *handle = cls;
1486 struct GNUNET_HashCode cache_key;
1487 struct GNUNET_TIME_Absolute *current_time;
1488 struct GNUNET_TIME_Absolute *last_time;
1489 char* cookie;
1490 char* header_val;
1491 json_t *root;
1492 json_error_t error;
1493 json_t *identity;
1494 char term_data[handle->rest_handle->data_size+1];
1495 term_data[handle->rest_handle->data_size] = '\0';
1496 GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1497 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1498 identity = json_object_get (root, "identity");
1499 if (!json_is_string (identity))
1500 {
1501 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1502 "Error parsing json string from %s\n",
1503 term_data);
1504 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1505 json_decref (root);
1506 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1507 return;
1508 }
1509 GNUNET_asprintf (&cookie,
1510 "Identity=%s",
1511 json_string_value (identity));
1512 GNUNET_asprintf (&header_val,
1513 "%s;Max-Age=%d",
1514 cookie,
1515 OIDC_COOKIE_EXPIRATION);
1516 MHD_add_response_header (resp, "Set-Cookie", header_val);
1517 MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1518 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1519
1520 if (0 != strcmp (json_string_value (identity),
1521 "Denied"))
1522 {
1523 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1524 *current_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1525 OIDC_COOKIE_EXPIRATION));
1526 last_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1527 GNUNET_free_non_null (last_time);
1528 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1529 &cache_key,
1530 current_time,
1531 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1532 }
1533 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1534 GNUNET_free (cookie);
1535 GNUNET_free (header_val);
1536 json_decref (root);
1537 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1538}
1539
1540static int
1541check_authorization (struct RequestHandle *handle,
1542 struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1543{
1544 struct GNUNET_HashCode cache_key;
1545 char *authorization;
1546 char *credentials;
1547 char *basic_authorization;
1548 char *client_id;
1549 char *pass;
1550 char *expected_pass;
1551 int client_exists = GNUNET_NO;
1552
1553 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1554 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1555 &cache_key);
1556 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1557 &cache_key) )
1558 {
1559 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1560 handle->edesc=GNUNET_strdup ("missing authorization");
1561 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1562 return GNUNET_SYSERR;
1563 }
1564 authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1565 &cache_key);
1566
1567 //split header in "Basic" and [content]
1568 credentials = strtok (authorization, " ");
1569 if (0 != strcmp ("Basic", credentials))
1570 {
1571 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1572 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1573 return GNUNET_SYSERR;
1574 }
1575 credentials = strtok (NULL, " ");
1576 if (NULL == credentials)
1577 {
1578 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1579 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1580 return GNUNET_SYSERR;
1581 }
1582 GNUNET_STRINGS_base64_decode (credentials,
1583 strlen (credentials),
1584 (void**)&basic_authorization);
1585
1586 if ( NULL == basic_authorization )
1587 {
1588 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1589 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1590 return GNUNET_SYSERR;
1591 }
1592 client_id = strtok (basic_authorization, ":");
1593 if ( NULL == client_id )
1594 {
1595 GNUNET_free_non_null (basic_authorization);
1596 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1597 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1598 return GNUNET_SYSERR;
1599 }
1600 pass = strtok (NULL, ":");
1601 if (NULL == pass)
1602 {
1603 GNUNET_free_non_null (basic_authorization);
1604 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1605 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1606 return GNUNET_SYSERR;
1607 }
1608
1609 //check client password
1610 if ( GNUNET_OK
1611 == GNUNET_CONFIGURATION_get_value_string (cfg,
1612 "reclaim-rest-plugin",
1613 "psw",
1614 &expected_pass) )
1615 {
1616 if (0 != strcmp (expected_pass, pass))
1617 {
1618 GNUNET_free_non_null (basic_authorization);
1619 GNUNET_free (expected_pass);
1620 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1621 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1622 return GNUNET_SYSERR;
1623 }
1624 GNUNET_free (expected_pass);
1625 }
1626 else
1627 {
1628 GNUNET_free_non_null (basic_authorization);
1629 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1630 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1631 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1632 return GNUNET_SYSERR;
1633 }
1634
1635 //check client_id
1636 for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry; )
1637 {
1638 if (0 == strcmp (handle->ego_entry->keystring, client_id))
1639 {
1640 client_exists = GNUNET_YES;
1641 break;
1642 }
1643 handle->ego_entry = handle->ego_entry->next;
1644 }
1645 if (GNUNET_NO == client_exists)
1646 {
1647 GNUNET_free_non_null (basic_authorization);
1648 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1649 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1650 return GNUNET_SYSERR;
1651 }
1652 GNUNET_STRINGS_string_to_data (client_id,
1653 strlen (client_id),
1654 cid,
1655 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1656
1657 GNUNET_free (basic_authorization);
1658 return GNUNET_OK;
1659}
1660
1661static int
1662ego_exists (struct RequestHandle *handle,
1663 struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1664{
1665 struct EgoEntry *ego_entry;
1666 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1667
1668 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1669 {
1670 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1671 if (0 == memcmp (&pub_key,
1672 test_key,
1673 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1674 {
1675 break;
1676 }
1677 }
1678 if (NULL == ego_entry)
1679 return GNUNET_NO;
1680 return GNUNET_YES;
1681}
1682
1683static void
1684store_ticket_reference (const struct RequestHandle *handle,
1685 const char* access_token,
1686 const struct GNUNET_RECLAIM_Ticket *ticket,
1687 const struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1688{
1689 struct GNUNET_HashCode cache_key;
1690 char *id_ticket_combination;
1691 char *ticket_string;
1692 char *client_id;
1693
1694 GNUNET_CRYPTO_hash (access_token,
1695 strlen (access_token),
1696 &cache_key);
1697 client_id = GNUNET_STRINGS_data_to_string_alloc (cid,
1698 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1699 ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket,
1700 sizeof (struct GNUNET_RECLAIM_Ticket));
1701 GNUNET_asprintf (&id_ticket_combination,
1702 "%s;%s",
1703 client_id,
1704 ticket_string);
1705 GNUNET_CONTAINER_multihashmap_put (OIDC_access_token_map,
1706 &cache_key,
1707 id_ticket_combination,
1708 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1709
1710 GNUNET_free (client_id);
1711 GNUNET_free (ticket_string);
1712}
1713
1714/**
1715 * Responds to token url-encoded POST request
1716 *
1717 * @param con_handle the connection handle
1718 * @param url the url
1719 * @param cls the RequestHandle
1720 */
1721static void
1722token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1723 const char* url,
1724 void *cls)
1725{
1726 struct RequestHandle *handle = cls;
1727 struct GNUNET_TIME_Relative expiration_time;
1728 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
1729 struct GNUNET_RECLAIM_Ticket *ticket;
1730 struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1731 struct GNUNET_HashCode cache_key;
1732 struct MHD_Response *resp;
1733 char *grant_type;
1734 char *code;
1735 char *json_response;
1736 char *id_token;
1737 char *access_token;
1738 char *jwt_secret;
1739 char *nonce;
1740 int i = 1;
1741
1742 /*
1743 * Check Authorization
1744 */
1745 if (GNUNET_SYSERR == check_authorization (handle,
1746 &cid))
1747 {
1748 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1749 "OIDC authorization for token endpoint failed\n");
1750 GNUNET_SCHEDULER_add_now (&do_error, handle);
1751 return;
1752 }
1753
1754 /*
1755 * Check parameter
1756 */
1757
1758 //TODO Do not allow multiple equal parameter names
1759 //REQUIRED grant_type
1760 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1761 if (GNUNET_NO ==
1762 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1763 &cache_key))
1764 {
1765 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1766 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1767 handle->response_code = MHD_HTTP_BAD_REQUEST;
1768 GNUNET_SCHEDULER_add_now (&do_error, handle);
1769 return;
1770 }
1771 grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1772 &cache_key);
1773
1774 //REQUIRED code
1775 GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1776 if (GNUNET_NO ==
1777 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1778 &cache_key))
1779 {
1780 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1781 handle->edesc = GNUNET_strdup ("missing parameter code");
1782 handle->response_code = MHD_HTTP_BAD_REQUEST;
1783 GNUNET_SCHEDULER_add_now (&do_error, handle);
1784 return;
1785 }
1786 code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1787 &cache_key);
1788
1789 //REQUIRED redirect_uri
1790 GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1791 &cache_key);
1792 if (GNUNET_NO ==
1793 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1794 &cache_key) )
1795 {
1796 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1797 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1798 handle->response_code = MHD_HTTP_BAD_REQUEST;
1799 GNUNET_SCHEDULER_add_now (&do_error, handle);
1800 return;
1801 }
1802
1803 //Check parameter grant_type == "authorization_code"
1804 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
1805 {
1806 handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1807 handle->response_code = MHD_HTTP_BAD_REQUEST;
1808 GNUNET_SCHEDULER_add_now (&do_error, handle);
1809 return;
1810 }
1811 GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1812 if (GNUNET_SYSERR ==
1813 GNUNET_CONTAINER_multihashmap_put (OIDC_used_ticket_map,
1814 &cache_key,
1815 &i,
1816 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1817 {
1818 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1819 handle->edesc = GNUNET_strdup ("Cannot use the same code more than once");
1820 handle->response_code = MHD_HTTP_BAD_REQUEST;
1821 GNUNET_SCHEDULER_add_now (&do_error, handle);
1822 return;
1823 }
1824
1825 //decode code
1826 if (GNUNET_OK != OIDC_parse_authz_code (&cid,
1827 code,
1828 &ticket,
1829 &nonce))
1830 {
1831 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1832 handle->edesc = GNUNET_strdup ("invalid code");
1833 handle->response_code = MHD_HTTP_BAD_REQUEST;
1834 GNUNET_SCHEDULER_add_now (&do_error, handle);
1835 return;
1836 }
1837
1838 //create jwt
1839 if (GNUNET_OK !=
1840 GNUNET_CONFIGURATION_get_value_time (cfg,
1841 "reclaim-rest-plugin",
1842 "expiration_time",
1843 &expiration_time))
1844 {
1845 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1846 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1847 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1848 GNUNET_SCHEDULER_add_now (&do_error, handle);
1849 GNUNET_free (ticket);
1850 return;
1851 }
1852
1853
1854 //TODO OPTIONAL acr,amr,azp
1855 if (GNUNET_NO == ego_exists (handle,
1856 &ticket->audience))
1857 {
1858 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1859 handle->edesc = GNUNET_strdup ("invalid code...");
1860 handle->response_code = MHD_HTTP_BAD_REQUEST;
1861 GNUNET_SCHEDULER_add_now (&do_error, handle);
1862 GNUNET_free (ticket);
1863 }
1864 if ( GNUNET_OK
1865 != GNUNET_CONFIGURATION_get_value_string (cfg,
1866 "reclaim-rest-plugin",
1867 "jwt_secret",
1868 &jwt_secret) )
1869 {
1870 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1871 handle->edesc = GNUNET_strdup ("No signing secret configured!");
1872 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1873 GNUNET_SCHEDULER_add_now (&do_error, handle);
1874 GNUNET_free (ticket);
1875 return;
1876 }
1877 //TODO We should collect the attributes here. cl always empty
1878 cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1879 id_token = OIDC_id_token_new (&ticket->audience,
1880 &ticket->identity,
1881 cl,
1882 &expiration_time,
1883 (NULL != nonce) ? nonce : NULL,
1884 jwt_secret);
1885 access_token = OIDC_access_token_new ();
1886 OIDC_build_token_response (access_token,
1887 id_token,
1888 &expiration_time,
1889 &json_response);
1890
1891 store_ticket_reference (handle,
1892 access_token,
1893 ticket,
1894 &cid);
1895 resp = GNUNET_REST_create_response (json_response);
1896 MHD_add_response_header (resp, "Cache-Control", "no-store");
1897 MHD_add_response_header (resp, "Pragma", "no-cache");
1898 MHD_add_response_header (resp, "Content-Type", "application/json");
1899 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1900 GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
1901 GNUNET_free (access_token);
1902 GNUNET_free (json_response);
1903 GNUNET_free (ticket);
1904 GNUNET_free (id_token);
1905 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1906}
1907
1908/**
1909 * Collects claims and stores them in handle
1910 */
1911static void
1912consume_ticket (void *cls,
1913 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1914 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1915{
1916 struct RequestHandle *handle = cls;
1917 char *tmp_value;
1918 json_t *value;
1919
1920 if (NULL == identity)
1921 {
1922 GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1923 return;
1924 }
1925 tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1926 attr->data,
1927 attr->data_size);
1928 value = json_string (tmp_value);
1929 json_object_set_new (handle->oidc->response,
1930 attr->name,
1931 value);
1932 GNUNET_free (tmp_value);
1933}
1934
1935/**
1936 * Responds to userinfo GET and url-encoded POST request
1937 *
1938 * @param con_handle the connection handle
1939 * @param url the url
1940 * @param cls the RequestHandle
1941 */
1942static void
1943userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1944 const char* url, void *cls)
1945{
1946 //TODO expiration time
1947 struct RequestHandle *handle = cls;
1948 char delimiter[] = " ";
1949 char delimiter_db[] = ";";
1950 struct GNUNET_HashCode cache_key;
1951 char *authorization, *authorization_type, *authorization_access_token;
1952 char *client_ticket, *client, *ticket_str;
1953 struct GNUNET_RECLAIM_Ticket *ticket;
1954
1955 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1956 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1957 &cache_key);
1958 if ( GNUNET_NO
1959 == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1960 &cache_key) )
1961 {
1962 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1963 handle->edesc = GNUNET_strdup ("No Access Token");
1964 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1965 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1966 return;
1967 }
1968 authorization = GNUNET_CONTAINER_multihashmap_get (
1969 handle->rest_handle->header_param_map, &cache_key);
1970
1971 //split header in "Bearer" and access_token
1972 authorization = GNUNET_strdup (authorization);
1973 authorization_type = strtok (authorization, delimiter);
1974 if ( 0 != strcmp ("Bearer", authorization_type) )
1975 {
1976 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1977 handle->edesc = GNUNET_strdup ("No Access Token");
1978 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1979 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1980 GNUNET_free (authorization);
1981 return;
1982 }
1983 authorization_access_token = strtok (NULL, delimiter);
1984 if ( NULL == authorization_access_token )
1985 {
1986 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1987 handle->edesc = GNUNET_strdup ("No Access Token");
1988 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1989 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1990 GNUNET_free (authorization);
1991 return;
1992 }
1993
1994 GNUNET_CRYPTO_hash (authorization_access_token,
1995 strlen (authorization_access_token),
1996 &cache_key);
1997 if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
1998 &cache_key) )
1999 {
2000 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2001 handle->edesc = GNUNET_strdup ("The Access Token expired");
2002 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2003 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2004 GNUNET_free (authorization);
2005 return;
2006 }
2007
2008 client_ticket = GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map,
2009 &cache_key);
2010 client_ticket = GNUNET_strdup (client_ticket);
2011 client = strtok (client_ticket,delimiter_db);
2012 if (NULL == client)
2013 {
2014 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2015 handle->edesc = GNUNET_strdup ("The Access Token expired");
2016 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2017 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2018 GNUNET_free (authorization);
2019 GNUNET_free (client_ticket);
2020 return;
2021 }
2022 handle->ego_entry = handle->ego_head;
2023 for (; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
2024 {
2025 if (0 == strcmp (handle->ego_entry->keystring,client))
2026 break;
2027 }
2028 if (NULL == handle->ego_entry)
2029 {
2030 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2031 handle->edesc = GNUNET_strdup ("The Access Token expired");
2032 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2033 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2034 GNUNET_free (authorization);
2035 GNUNET_free (client_ticket);
2036 return;
2037 }
2038 ticket_str = strtok (NULL, delimiter_db);
2039 if (NULL == ticket_str)
2040 {
2041 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2042 handle->edesc = GNUNET_strdup ("The Access Token expired");
2043 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2044 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2045 GNUNET_free (authorization);
2046 GNUNET_free (client_ticket);
2047 return;
2048 }
2049 ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
2050 if ( GNUNET_OK
2051 != GNUNET_STRINGS_string_to_data (ticket_str,
2052 strlen (ticket_str),
2053 ticket,
2054 sizeof (struct GNUNET_RECLAIM_Ticket)))
2055 {
2056 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2057 handle->edesc = GNUNET_strdup ("The Access Token expired");
2058 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2059 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2060 GNUNET_free (ticket);
2061 GNUNET_free (authorization);
2062 GNUNET_free (client_ticket);
2063 return;
2064 }
2065
2066 handle->idp = GNUNET_RECLAIM_connect (cfg);
2067 handle->oidc->response = json_object ();
2068 json_object_set_new (handle->oidc->response,
2069 "sub",
2070 json_string (handle->ego_entry->keystring));
2071 handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
2072 GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
2073 ticket,
2074 consume_ticket,
2075 handle);
2076 GNUNET_free (ticket);
2077 GNUNET_free (authorization);
2078 GNUNET_free (client_ticket);
2079
2080}
2081
2082
2083/**
2084 * Handle rest request
2085 *
2086 * @param handle the request handle
2087 */
2088static void
2089init_cont (struct RequestHandle *handle)
2090{
2091 struct GNUNET_REST_RequestHandlerError err;
2092 static const struct GNUNET_REST_RequestHandler handlers[] = {
2093 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
2094 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
2095 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
2096 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2097 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2098 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2099 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
2100 &options_cont},
2101 GNUNET_REST_HANDLER_END
2102 };
2103
2104 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
2105 handlers,
2106 &err,
2107 handle))
2108 {
2109 handle->response_code = err.error_code;
2110 GNUNET_SCHEDULER_add_now (&do_error, handle);
2111 }
2112}
2113
2114/**
2115 * If listing is enabled, prints information about the egos.
2116 *
2117 * This function is initially called for all egos and then again
2118 * whenever a ego's identifier changes or if it is deleted. At the
2119 * end of the initial pass over all egos, the function is once called
2120 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2121 * be invoked in the future or that there was an error.
2122 *
2123 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2124 * this function is only called ONCE, and 'NULL' being passed in
2125 * 'ego' does indicate an error (i.e. name is taken or no default
2126 * value is known). If 'ego' is non-NULL and if '*ctx'
2127 * is set in those callbacks, the value WILL be passed to a subsequent
2128 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2129 * that one was not NULL).
2130 *
2131 * When an identity is renamed, this function is called with the
2132 * (known) ego but the NEW identifier.
2133 *
2134 * When an identity is deleted, this function is called with the
2135 * (known) ego and "NULL" for the 'identifier'. In this case,
2136 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2137 * cleaned up).
2138 *
2139 * @param cls closure
2140 * @param ego ego handle
2141 * @param ctx context for application to store data for this ego
2142 * (during the lifetime of this process, initially NULL)
2143 * @param identifier identifier assigned by the user for this ego,
2144 * NULL if the user just deleted the ego and it
2145 * must thus no longer be used
2146 */
2147static void
2148list_ego (void *cls,
2149 struct GNUNET_IDENTITY_Ego *ego,
2150 void **ctx,
2151 const char *identifier)
2152{
2153 struct RequestHandle *handle = cls;
2154 struct EgoEntry *ego_entry;
2155 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2156
2157 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2158 {
2159 handle->state = ID_REST_STATE_POST_INIT;
2160 init_cont (handle);
2161 return;
2162 }
2163 if (ID_REST_STATE_INIT == handle->state) {
2164 ego_entry = GNUNET_new (struct EgoEntry);
2165 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2166 ego_entry->keystring =
2167 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2168 ego_entry->ego = ego;
2169 ego_entry->identifier = GNUNET_strdup (identifier);
2170 GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2171 handle->ego_tail,
2172 ego_entry);
2173 return;
2174 }
2175 /* Ego renamed or added */
2176 if (identifier != NULL) {
2177 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2178 if (ego_entry->ego == ego) {
2179 /* Rename */
2180 GNUNET_free (ego_entry->identifier);
2181 ego_entry->identifier = GNUNET_strdup (identifier);
2182 break;
2183 }
2184 }
2185 if (NULL == ego_entry) {
2186 /* Add */
2187 ego_entry = GNUNET_new (struct EgoEntry);
2188 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2189 ego_entry->keystring =
2190 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2191 ego_entry->ego = ego;
2192 ego_entry->identifier = GNUNET_strdup (identifier);
2193 GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2194 handle->ego_tail,
2195 ego_entry);
2196 }
2197 } else {
2198 /* Delete */
2199 for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2200 if (ego_entry->ego == ego)
2201 break;
2202 }
2203 if (NULL != ego_entry)
2204 GNUNET_CONTAINER_DLL_remove (handle->ego_head,handle->ego_tail, ego_entry);
2205 }
2206
2207}
2208
2209static void
2210rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2211 GNUNET_REST_ResultProcessor proc,
2212 void *proc_cls)
2213{
2214 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2215 handle->oidc = GNUNET_new (struct OIDC_Variables);
2216 if (NULL == OIDC_cookie_jar_map)
2217 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2218 if (NULL == OIDC_identity_grants)
2219 OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2220 if (NULL == OIDC_used_ticket_map)
2221 OIDC_used_ticket_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2222 if (NULL == OIDC_access_token_map)
2223 OIDC_access_token_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2224 handle->response_code = 0;
2225 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2226 handle->proc_cls = proc_cls;
2227 handle->proc = proc;
2228 handle->state = ID_REST_STATE_INIT;
2229 handle->rest_handle = rest_handle;
2230
2231 handle->url = GNUNET_strdup (rest_handle->url);
2232 if (handle->url[strlen (handle->url)-1] == '/')
2233 handle->url[strlen (handle->url)-1] = '\0';
2234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2235 "Connecting...\n");
2236 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2237 &list_ego,
2238 handle);
2239 handle->gns_handle = GNUNET_GNS_connect (cfg);
2240 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2241 handle->timeout_task =
2242 GNUNET_SCHEDULER_add_delayed (handle->timeout,
2243 &do_timeout,
2244 handle);
2245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2246 "Connected\n");
2247}
2248
2249/**
2250 * Entry point for the plugin.
2251 *
2252 * @param cls Config info
2253 * @return NULL on error, otherwise the plugin context
2254 */
2255void *
2256libgnunet_plugin_rest_openid_connect_init (void *cls)
2257{
2258 static struct Plugin plugin;
2259 struct GNUNET_REST_Plugin *api;
2260
2261 cfg = cls;
2262 if (NULL != plugin.cfg)
2263 return NULL; /* can only initialize once! */
2264 memset (&plugin, 0, sizeof (struct Plugin));
2265 plugin.cfg = cfg;
2266 api = GNUNET_new (struct GNUNET_REST_Plugin);
2267 api->cls = &plugin;
2268 api->name = GNUNET_REST_API_NS_OIDC;
2269 api->process_request = &rest_identity_process_request;
2270 GNUNET_asprintf (&allow_methods,
2271 "%s, %s, %s, %s, %s",
2272 MHD_HTTP_METHOD_GET,
2273 MHD_HTTP_METHOD_POST,
2274 MHD_HTTP_METHOD_PUT,
2275 MHD_HTTP_METHOD_DELETE,
2276 MHD_HTTP_METHOD_OPTIONS);
2277
2278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2279 _("Identity Provider REST API initialized\n"));
2280 return api;
2281}
2282
2283
2284/**
2285 * Exit point from the plugin.
2286 *
2287 * @param cls the plugin context (as returned by "init")
2288 * @return always NULL
2289 */
2290void *
2291libgnunet_plugin_rest_openid_connect_done (void *cls)
2292{
2293 struct GNUNET_REST_Plugin *api = cls;
2294 struct Plugin *plugin = api->cls;
2295 plugin->cfg = NULL;
2296
2297 struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2298 void *value = NULL;
2299 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2300 while (GNUNET_YES ==
2301 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2302 GNUNET_free_non_null (value);
2303 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2304
2305 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2306 while (GNUNET_YES ==
2307 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2308 GNUNET_free_non_null (value);
2309 GNUNET_CONTAINER_multihashmap_destroy (OIDC_identity_grants);
2310
2311 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_used_ticket_map);
2312 while (GNUNET_YES ==
2313 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2314 GNUNET_free_non_null (value);
2315 GNUNET_CONTAINER_multihashmap_destroy (OIDC_used_ticket_map);
2316
2317 hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2318 while (GNUNET_YES ==
2319 GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2320 GNUNET_free_non_null (value);
2321 GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2322 GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2323 GNUNET_free_non_null (allow_methods);
2324 GNUNET_free (api);
2325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2326 "Identity Provider REST plugin is finished\n");
2327 return NULL;
2328}
2329
2330/* end of plugin_rest_identity_provider.c */
diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c
new file mode 100644
index 000000000..b36ed2bb6
--- /dev/null
+++ b/src/reclaim/plugin_rest_reclaim.c
@@ -0,0 +1,1104 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 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 * @file reclaim/plugin_rest_reclaim.c
24 * @brief GNUnet reclaim REST plugin
25 *
26 */
27
28#include "platform.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_identity_service.h"
31#include "gnunet_gns_service.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_namestore_service.h"
34#include "gnunet_rest_lib.h"
35#include "microhttpd.h"
36#include <jansson.h>
37#include <inttypes.h>
38#include "gnunet_signatures.h"
39#include "gnunet_reclaim_attribute_lib.h"
40#include "gnunet_reclaim_service.h"
41#include "json_reclaim.h"
42
43/**
44 * REST root namespace
45 */
46#define GNUNET_REST_API_NS_RECLAIM "/reclaim"
47
48/**
49 * Attribute namespace
50 */
51#define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes"
52
53/**
54 * Ticket namespace
55 */
56#define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets"
57
58/**
59 * Revoke namespace
60 */
61#define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke"
62
63/**
64 * Revoke namespace
65 */
66#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume"
67
68/**
69 * State while collecting all egos
70 */
71#define ID_REST_STATE_INIT 0
72
73/**
74 * Done collecting egos
75 */
76#define ID_REST_STATE_POST_INIT 1
77
78/**
79 * The configuration handle
80 */
81const struct GNUNET_CONFIGURATION_Handle *cfg;
82
83/**
84 * HTTP methods allows for this plugin
85 */
86static char* allow_methods;
87
88/**
89 * @brief struct returned by the initialization function of the plugin
90 */
91struct Plugin
92{
93 const struct GNUNET_CONFIGURATION_Handle *cfg;
94};
95
96/**
97 * The ego list
98 */
99struct EgoEntry
100{
101 /**
102 * DLL
103 */
104 struct EgoEntry *next;
105
106 /**
107 * DLL
108 */
109 struct EgoEntry *prev;
110
111 /**
112 * Ego Identifier
113 */
114 char *identifier;
115
116 /**
117 * Public key string
118 */
119 char *keystring;
120
121 /**
122 * The Ego
123 */
124 struct GNUNET_IDENTITY_Ego *ego;
125};
126
127
128struct RequestHandle
129{
130 /**
131 * Ego list
132 */
133 struct EgoEntry *ego_head;
134
135 /**
136 * Ego list
137 */
138 struct EgoEntry *ego_tail;
139
140 /**
141 * Selected ego
142 */
143 struct EgoEntry *ego_entry;
144
145 /**
146 * Pointer to ego private key
147 */
148 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
149
150 /**
151 * The processing state
152 */
153 int state;
154
155 /**
156 * Handle to Identity service.
157 */
158 struct GNUNET_IDENTITY_Handle *identity_handle;
159
160 /**
161 * Rest connection
162 */
163 struct GNUNET_REST_RequestHandle *rest_handle;
164
165 /**
166 * Handle to NAMESTORE
167 */
168 struct GNUNET_NAMESTORE_Handle *namestore_handle;
169
170 /**
171 * Iterator for NAMESTORE
172 */
173 struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
174
175 /**
176 * Attribute claim list
177 */
178 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
179
180 /**
181 * IDENTITY Operation
182 */
183 struct GNUNET_IDENTITY_Operation *op;
184
185 /**
186 * Identity Provider
187 */
188 struct GNUNET_RECLAIM_Handle *idp;
189
190 /**
191 * Idp Operation
192 */
193 struct GNUNET_RECLAIM_Operation *idp_op;
194
195 /**
196 * Attribute iterator
197 */
198 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
199
200 /**
201 * Ticket iterator
202 */
203 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
204
205 /**
206 * A ticket
207 */
208 struct GNUNET_RECLAIM_Ticket ticket;
209
210 /**
211 * Desired timeout for the lookup (default is no timeout).
212 */
213 struct GNUNET_TIME_Relative timeout;
214
215 /**
216 * ID of a task associated with the resolution process.
217 */
218 struct GNUNET_SCHEDULER_Task *timeout_task;
219
220 /**
221 * The plugin result processor
222 */
223 GNUNET_REST_ResultProcessor proc;
224
225 /**
226 * The closure of the result processor
227 */
228 void *proc_cls;
229
230 /**
231 * The url
232 */
233 char *url;
234
235 /**
236 * Error response message
237 */
238 char *emsg;
239
240 /**
241 * Reponse code
242 */
243 int response_code;
244
245 /**
246 * Response object
247 */
248 json_t *resp_object;
249
250};
251
252/**
253 * Cleanup lookup handle
254 * @param handle Handle to clean up
255 */
256static void
257cleanup_handle (struct RequestHandle *handle)
258{
259 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
260 struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
261 struct EgoEntry *ego_entry;
262 struct EgoEntry *ego_tmp;
263 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264 "Cleaning up\n");
265 if (NULL != handle->resp_object)
266 json_decref (handle->resp_object);
267 if (NULL != handle->timeout_task)
268 GNUNET_SCHEDULER_cancel (handle->timeout_task);
269 if (NULL != handle->identity_handle)
270 GNUNET_IDENTITY_disconnect (handle->identity_handle);
271 if (NULL != handle->attr_it)
272 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
273 if (NULL != handle->ticket_it)
274 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
275 if (NULL != handle->idp)
276 GNUNET_RECLAIM_disconnect (handle->idp);
277 if (NULL != handle->url)
278 GNUNET_free (handle->url);
279 if (NULL != handle->emsg)
280 GNUNET_free (handle->emsg);
281 if (NULL != handle->namestore_handle)
282 GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
283 if ( NULL != handle->attr_list )
284 {
285 for (claim_entry = handle->attr_list->list_head;
286 NULL != claim_entry;)
287 {
288 claim_tmp = claim_entry;
289 claim_entry = claim_entry->next;
290 GNUNET_free(claim_tmp->claim);
291 GNUNET_free(claim_tmp);
292 }
293 GNUNET_free (handle->attr_list);
294 }
295 for (ego_entry = handle->ego_head;
296 NULL != ego_entry;)
297 {
298 ego_tmp = ego_entry;
299 ego_entry = ego_entry->next;
300 GNUNET_free (ego_tmp->identifier);
301 GNUNET_free (ego_tmp->keystring);
302 GNUNET_free (ego_tmp);
303 }
304 if (NULL != handle->attr_it)
305 {
306 GNUNET_free(handle->attr_it);
307 }
308 GNUNET_free (handle);
309}
310
311static void
312cleanup_handle_delayed (void *cls)
313{
314 cleanup_handle (cls);
315}
316
317
318/**
319 * Task run on error, sends error message. Cleans up everything.
320 *
321 * @param cls the `struct RequestHandle`
322 */
323static void
324do_error (void *cls)
325{
326 struct RequestHandle *handle = cls;
327 struct MHD_Response *resp;
328 char *json_error;
329
330 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }",
331 handle->emsg);
332 if ( 0 == handle->response_code )
333 {
334 handle->response_code = MHD_HTTP_BAD_REQUEST;
335 }
336 resp = GNUNET_REST_create_response (json_error);
337 MHD_add_response_header (resp, "Content-Type", "application/json");
338 handle->proc (handle->proc_cls, resp, handle->response_code);
339 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
340 GNUNET_free (json_error);
341}
342
343
344/**
345 * Task run on timeout, sends error message. Cleans up everything.
346 *
347 * @param cls the `struct RequestHandle`
348 */
349static void
350do_timeout (void *cls)
351{
352 struct RequestHandle *handle = cls;
353
354 handle->timeout_task = NULL;
355 do_error (handle);
356}
357
358
359static void
360collect_error_cb (void *cls)
361{
362 struct RequestHandle *handle = cls;
363
364 do_error (handle);
365}
366
367static void
368finished_cont (void *cls,
369 int32_t success,
370 const char *emsg)
371{
372 struct RequestHandle *handle = cls;
373 struct MHD_Response *resp;
374
375 resp = GNUNET_REST_create_response (emsg);
376 if (GNUNET_OK != success)
377 {
378 GNUNET_SCHEDULER_add_now (&do_error, handle);
379 return;
380 }
381 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
382 GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
383}
384
385
386/**
387 * Return attributes for identity
388 *
389 * @param cls the request handle
390 */
391static void
392return_response (void *cls)
393{
394 char* result_str;
395 struct RequestHandle *handle = cls;
396 struct MHD_Response *resp;
397
398 result_str = json_dumps (handle->resp_object, 0);
399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
400 resp = GNUNET_REST_create_response (result_str);
401 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
402 GNUNET_free (result_str);
403 cleanup_handle (handle);
404}
405
406static void
407collect_finished_cb (void *cls)
408{
409 struct RequestHandle *handle = cls;
410 //Done
411 handle->attr_it = NULL;
412 handle->ticket_it = NULL;
413 GNUNET_SCHEDULER_add_now (&return_response, handle);
414}
415
416
417/**
418 * Collect all attributes for an ego
419 *
420 */
421static void
422ticket_collect (void *cls,
423 const struct GNUNET_RECLAIM_Ticket *ticket)
424{
425 json_t *json_resource;
426 struct RequestHandle *handle = cls;
427 json_t *value;
428 char* tmp;
429
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
431 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
432 sizeof (uint64_t));
433 json_resource = json_object ();
434 GNUNET_free (tmp);
435 json_array_append (handle->resp_object,
436 json_resource);
437
438 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
439 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
440 value = json_string (tmp);
441 json_object_set_new (json_resource,
442 "issuer",
443 value);
444 GNUNET_free (tmp);
445 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
446 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
447 value = json_string (tmp);
448 json_object_set_new (json_resource,
449 "audience",
450 value);
451 GNUNET_free (tmp);
452 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
453 sizeof (uint64_t));
454 value = json_string (tmp);
455 json_object_set_new (json_resource,
456 "rnd",
457 value);
458 GNUNET_free (tmp);
459 GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it);
460}
461
462
463
464/**
465 * List tickets for identity request
466 *
467 * @param con_handle the connection handle
468 * @param url the url
469 * @param cls the RequestHandle
470 */
471static void
472list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
473 const char* url,
474 void *cls)
475{
476 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
477 struct RequestHandle *handle = cls;
478 struct EgoEntry *ego_entry;
479 char *identity;
480
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
482 handle->url);
483 if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
484 strlen (handle->url))
485 {
486 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
487 GNUNET_SCHEDULER_add_now (&do_error, handle);
488 return;
489 }
490 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
491
492 for (ego_entry = handle->ego_head;
493 NULL != ego_entry;
494 ego_entry = ego_entry->next)
495 if (0 == strcmp (identity, ego_entry->identifier))
496 break;
497 handle->resp_object = json_array ();
498
499 if (NULL == ego_entry)
500 {
501 //Done
502 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
503 identity);
504 GNUNET_SCHEDULER_add_now (&return_response, handle);
505 return;
506 }
507 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
508 handle->idp = GNUNET_RECLAIM_connect (cfg);
509 handle->ticket_it = GNUNET_RECLAIM_ticket_iteration_start (handle->idp,
510 priv_key,
511 &collect_error_cb,
512 handle,
513 &ticket_collect,
514 handle,
515 &collect_finished_cb,
516 handle);
517}
518
519
520static void
521add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
522 const char* url,
523 void *cls)
524{
525 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
526 const char* identity;
527 struct RequestHandle *handle = cls;
528 struct EgoEntry *ego_entry;
529 struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attribute;
530 struct GNUNET_TIME_Relative exp;
531 char term_data[handle->rest_handle->data_size+1];
532 json_t *data_json;
533 json_error_t err;
534 struct GNUNET_JSON_Specification attrspec[] = {
535 GNUNET_RECLAIM_JSON_spec_claim (&attribute),
536 GNUNET_JSON_spec_end()
537 };
538
539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
540 handle->url);
541 if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >=
542 strlen (handle->url))
543 {
544 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
545 GNUNET_SCHEDULER_add_now (&do_error, handle);
546 return;
547 }
548 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
549
550 for (ego_entry = handle->ego_head;
551 NULL != ego_entry;
552 ego_entry = ego_entry->next)
553 if (0 == strcmp (identity, ego_entry->identifier))
554 break;
555
556 if (NULL == ego_entry)
557 {
558 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
559 "Identity unknown (%s)\n", identity);
560 return;
561 }
562 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
563
564 if (0 >= handle->rest_handle->data_size)
565 {
566 GNUNET_SCHEDULER_add_now (&do_error, handle);
567 return;
568 }
569
570 term_data[handle->rest_handle->data_size] = '\0';
571 GNUNET_memcpy (term_data,
572 handle->rest_handle->data,
573 handle->rest_handle->data_size);
574 data_json = json_loads (term_data,
575 JSON_DECODE_ANY,
576 &err);
577 GNUNET_assert (GNUNET_OK ==
578 GNUNET_JSON_parse (data_json, attrspec,
579 NULL, NULL));
580 json_decref (data_json);
581 if (NULL == attribute)
582 {
583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
584 "Unable to parse attribute from %s\n",
585 term_data);
586 GNUNET_SCHEDULER_add_now (&do_error, handle);
587 return;
588 }
589 handle->idp = GNUNET_RECLAIM_connect (cfg);
590 exp = GNUNET_TIME_UNIT_HOURS;
591 handle->idp_op = GNUNET_RECLAIM_attribute_store (handle->idp,
592 identity_priv,
593 attribute,
594 &exp,
595 &finished_cont,
596 handle);
597 GNUNET_JSON_parse_free (attrspec);
598}
599
600
601
602/**
603 * Collect all attributes for an ego
604 *
605 */
606static void
607attr_collect (void *cls,
608 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
609 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
610{
611 struct RequestHandle *handle = cls;
612 json_t *attr_obj;
613 const char* type;
614 char* tmp_value;
615
616 if ((NULL == attr->name) || (NULL == attr->data))
617 {
618 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
619 return;
620 }
621
622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
623 attr->name);
624
625 tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
626 attr->data,
627 attr->data_size);
628
629 attr_obj = json_object ();
630 json_object_set_new (attr_obj,
631 "value",
632 json_string (tmp_value));
633 json_object_set_new (attr_obj,
634 "name",
635 json_string (attr->name));
636 type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type);
637 json_object_set_new (attr_obj,
638 "type",
639 json_string (type));
640 json_array_append (handle->resp_object,
641 attr_obj);
642 json_decref (attr_obj);
643 GNUNET_free(tmp_value);
644 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
645}
646
647
648
649/**
650 * List attributes for identity request
651 *
652 * @param con_handle the connection handle
653 * @param url the url
654 * @param cls the RequestHandle
655 */
656static void
657list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
658 const char* url,
659 void *cls)
660{
661 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
662 struct RequestHandle *handle = cls;
663 struct EgoEntry *ego_entry;
664 char *identity;
665
666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
667 handle->url);
668 if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >=
669 strlen (handle->url))
670 {
671 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
672 GNUNET_SCHEDULER_add_now (&do_error, handle);
673 return;
674 }
675 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
676
677 for (ego_entry = handle->ego_head;
678 NULL != ego_entry;
679 ego_entry = ego_entry->next)
680 if (0 == strcmp (identity, ego_entry->identifier))
681 break;
682 handle->resp_object = json_array ();
683
684
685 if (NULL == ego_entry)
686 {
687 //Done
688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
689 identity);
690 GNUNET_SCHEDULER_add_now (&return_response, handle);
691 return;
692 }
693 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
694 handle->idp = GNUNET_RECLAIM_connect (cfg);
695 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp,
696 priv_key,
697 &collect_error_cb,
698 handle,
699 &attr_collect,
700 handle,
701 &collect_finished_cb,
702 handle);
703}
704
705
706static void
707revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
708 const char* url,
709 void *cls)
710{
711 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
712 struct RequestHandle *handle = cls;
713 struct EgoEntry *ego_entry;
714 struct GNUNET_RECLAIM_Ticket *ticket;
715 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
716 char term_data[handle->rest_handle->data_size+1];
717 json_t *data_json;
718 json_error_t err;
719 struct GNUNET_JSON_Specification tktspec[] = {
720 GNUNET_RECLAIM_JSON_spec_ticket (&ticket),
721 GNUNET_JSON_spec_end()
722 };
723
724 if (0 >= handle->rest_handle->data_size)
725 {
726 GNUNET_SCHEDULER_add_now (&do_error, handle);
727 return;
728 }
729
730 term_data[handle->rest_handle->data_size] = '\0';
731 GNUNET_memcpy (term_data,
732 handle->rest_handle->data,
733 handle->rest_handle->data_size);
734 data_json = json_loads (term_data,
735 JSON_DECODE_ANY,
736 &err);
737 GNUNET_assert (GNUNET_OK ==
738 GNUNET_JSON_parse (data_json, tktspec,
739 NULL, NULL));
740 json_decref (data_json);
741 if (NULL == ticket)
742 {
743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744 "Unable to parse ticket from %s\n",
745 term_data);
746 GNUNET_SCHEDULER_add_now (&do_error, handle);
747 return;
748 }
749 if (GNUNET_OK != GNUNET_JSON_parse (data_json,
750 tktspec,
751 NULL, NULL))
752 {
753 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
754 GNUNET_SCHEDULER_add_now (&do_error, handle);
755 GNUNET_JSON_parse_free (tktspec);
756 json_decref (data_json);
757 return;
758 }
759
760 for (ego_entry = handle->ego_head;
761 NULL != ego_entry;
762 ego_entry = ego_entry->next)
763 {
764 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
765 &tmp_pk);
766 if (0 == memcmp (&ticket->identity,
767 &tmp_pk,
768 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
769 break;
770 }
771 if (NULL == ego_entry)
772 {
773 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
774 "Identity unknown\n");
775 GNUNET_JSON_parse_free (tktspec);
776 return;
777 }
778 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
779
780 handle->idp = GNUNET_RECLAIM_connect (cfg);
781 handle->idp_op = GNUNET_RECLAIM_ticket_revoke (handle->idp,
782 identity_priv,
783 ticket,
784 &finished_cont,
785 handle);
786 GNUNET_JSON_parse_free (tktspec);
787}
788
789static void
790consume_cont (void *cls,
791 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
792 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
793{
794 struct RequestHandle *handle = cls;
795 char *val_str;
796 json_t *value;
797
798 if (NULL == identity)
799 {
800 GNUNET_SCHEDULER_add_now (&return_response, handle);
801 return;
802 }
803
804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
805 attr->name);
806 val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
807 attr->data,
808 attr->data_size);
809 if (NULL == val_str)
810 {
811 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse value for: %s\n",
812 attr->name);
813 return;
814 }
815 value = json_string(val_str);
816 json_object_set_new (handle->resp_object,
817 attr->name,
818 value);
819 json_decref (value);
820 GNUNET_free (val_str);
821}
822
823static void
824consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
825 const char* url,
826 void *cls)
827{
828 const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
829 struct RequestHandle *handle = cls;
830 struct EgoEntry *ego_entry;
831 struct GNUNET_RECLAIM_Ticket *ticket;
832 struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
833 char term_data[handle->rest_handle->data_size+1];
834 json_t *data_json;
835 json_error_t err;
836 struct GNUNET_JSON_Specification tktspec[] = {
837 GNUNET_RECLAIM_JSON_spec_ticket (&ticket),
838 GNUNET_JSON_spec_end ()
839 };
840
841 if (0 >= handle->rest_handle->data_size)
842 {
843 GNUNET_SCHEDULER_add_now (&do_error, handle);
844 return;
845 }
846
847 term_data[handle->rest_handle->data_size] = '\0';
848 GNUNET_memcpy (term_data,
849 handle->rest_handle->data,
850 handle->rest_handle->data_size);
851 data_json = json_loads (term_data,
852 JSON_DECODE_ANY,
853 &err);
854 if (NULL == data_json)
855 {
856 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
857 "Unable to parse JSON Object from %s\n",
858 term_data);
859 GNUNET_SCHEDULER_add_now (&do_error, handle);
860 return;
861 }
862 if (GNUNET_OK != GNUNET_JSON_parse (data_json,
863 tktspec,
864 NULL, NULL))
865 {
866 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
867 GNUNET_SCHEDULER_add_now (&do_error, handle);
868 GNUNET_JSON_parse_free(tktspec);
869 json_decref (data_json);
870 return;
871 }
872 for (ego_entry = handle->ego_head;
873 NULL != ego_entry;
874 ego_entry = ego_entry->next)
875 {
876 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
877 &tmp_pk);
878 if (0 == memcmp (&ticket->audience,
879 &tmp_pk,
880 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
881 break;
882 }
883 if (NULL == ego_entry)
884 {
885 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
886 "Identity unknown\n");
887 GNUNET_JSON_parse_free (tktspec);
888 return;
889 }
890 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
891 handle->resp_object = json_object ();
892 handle->idp = GNUNET_RECLAIM_connect (cfg);
893 handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
894 identity_priv,
895 ticket,
896 &consume_cont,
897 handle);
898 GNUNET_JSON_parse_free (tktspec);
899}
900
901
902
903/**
904 * Respond to OPTIONS request
905 *
906 * @param con_handle the connection handle
907 * @param url the url
908 * @param cls the RequestHandle
909 */
910static void
911options_cont (struct GNUNET_REST_RequestHandle *con_handle,
912 const char* url,
913 void *cls)
914{
915 struct MHD_Response *resp;
916 struct RequestHandle *handle = cls;
917
918 //For now, independent of path return all options
919 resp = GNUNET_REST_create_response (NULL);
920 MHD_add_response_header (resp,
921 "Access-Control-Allow-Methods",
922 allow_methods);
923 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
924 cleanup_handle (handle);
925 return;
926}
927
928/**
929 * Handle rest request
930 *
931 * @param handle the request handle
932 */
933static void
934init_cont (struct RequestHandle *handle)
935{
936 struct GNUNET_REST_RequestHandlerError err;
937 static const struct GNUNET_REST_RequestHandler handlers[] = {
938 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont},
939 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont},
940 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
941 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
942 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
943 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM,
944 &options_cont},
945 GNUNET_REST_HANDLER_END
946 };
947
948 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
949 handlers,
950 &err,
951 handle))
952 {
953 handle->response_code = err.error_code;
954 GNUNET_SCHEDULER_add_now (&do_error, handle);
955 }
956}
957
958/**
959 * If listing is enabled, prints information about the egos.
960 *
961 * This function is initially called for all egos and then again
962 * whenever a ego's identifier changes or if it is deleted. At the
963 * end of the initial pass over all egos, the function is once called
964 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
965 * be invoked in the future or that there was an error.
966 *
967 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
968 * this function is only called ONCE, and 'NULL' being passed in
969 * 'ego' does indicate an error (i.e. name is taken or no default
970 * value is known). If 'ego' is non-NULL and if '*ctx'
971 * is set in those callbacks, the value WILL be passed to a subsequent
972 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
973 * that one was not NULL).
974 *
975 * When an identity is renamed, this function is called with the
976 * (known) ego but the NEW identifier.
977 *
978 * When an identity is deleted, this function is called with the
979 * (known) ego and "NULL" for the 'identifier'. In this case,
980 * the 'ego' is henceforth invalid (and the 'ctx' should also be
981 * cleaned up).
982 *
983 * @param cls closure
984 * @param ego ego handle
985 * @param ctx context for application to store data for this ego
986 * (during the lifetime of this process, initially NULL)
987 * @param identifier identifier assigned by the user for this ego,
988 * NULL if the user just deleted the ego and it
989 * must thus no longer be used
990 */
991static void
992list_ego (void *cls,
993 struct GNUNET_IDENTITY_Ego *ego,
994 void **ctx,
995 const char *identifier)
996{
997 struct RequestHandle *handle = cls;
998 struct EgoEntry *ego_entry;
999 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1000
1001 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1002 {
1003 handle->state = ID_REST_STATE_POST_INIT;
1004 init_cont (handle);
1005 return;
1006 }
1007 if (ID_REST_STATE_INIT == handle->state) {
1008 ego_entry = GNUNET_new (struct EgoEntry);
1009 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1010 ego_entry->keystring =
1011 GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1012 ego_entry->ego = ego;
1013 ego_entry->identifier = GNUNET_strdup (identifier);
1014 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1015 }
1016
1017}
1018
1019static void
1020rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1021 GNUNET_REST_ResultProcessor proc,
1022 void *proc_cls)
1023{
1024 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1025 handle->response_code = 0;
1026 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1027 handle->proc_cls = proc_cls;
1028 handle->proc = proc;
1029 handle->state = ID_REST_STATE_INIT;
1030 handle->rest_handle = rest_handle;
1031
1032 handle->url = GNUNET_strdup (rest_handle->url);
1033 if (handle->url[strlen (handle->url)-1] == '/')
1034 handle->url[strlen (handle->url)-1] = '\0';
1035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1036 "Connecting...\n");
1037 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1038 &list_ego,
1039 handle);
1040 handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
1041 handle->timeout_task =
1042 GNUNET_SCHEDULER_add_delayed (handle->timeout,
1043 &do_timeout,
1044 handle);
1045 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1046 "Connected\n");
1047}
1048
1049/**
1050 * Entry point for the plugin.
1051 *
1052 * @param cls Config info
1053 * @return NULL on error, otherwise the plugin context
1054 */
1055void *
1056libgnunet_plugin_rest_reclaim_init (void *cls)
1057{
1058 static struct Plugin plugin;
1059 struct GNUNET_REST_Plugin *api;
1060
1061 cfg = cls;
1062 if (NULL != plugin.cfg)
1063 return NULL; /* can only initialize once! */
1064 memset (&plugin, 0, sizeof (struct Plugin));
1065 plugin.cfg = cfg;
1066 api = GNUNET_new (struct GNUNET_REST_Plugin);
1067 api->cls = &plugin;
1068 api->name = GNUNET_REST_API_NS_RECLAIM;
1069 api->process_request = &rest_identity_process_request;
1070 GNUNET_asprintf (&allow_methods,
1071 "%s, %s, %s, %s, %s",
1072 MHD_HTTP_METHOD_GET,
1073 MHD_HTTP_METHOD_POST,
1074 MHD_HTTP_METHOD_PUT,
1075 MHD_HTTP_METHOD_DELETE,
1076 MHD_HTTP_METHOD_OPTIONS);
1077
1078 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1079 _("Identity Provider REST API initialized\n"));
1080 return api;
1081}
1082
1083
1084/**
1085 * Exit point from the plugin.
1086 *
1087 * @param cls the plugin context (as returned by "init")
1088 * @return always NULL
1089 */
1090void *
1091libgnunet_plugin_rest_reclaim_done (void *cls)
1092{
1093 struct GNUNET_REST_Plugin *api = cls;
1094 struct Plugin *plugin = api->cls;
1095 plugin->cfg = NULL;
1096
1097 GNUNET_free_non_null (allow_methods);
1098 GNUNET_free (api);
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100 "Identity Provider REST plugin is finished\n");
1101 return NULL;
1102}
1103
1104/* end of plugin_rest_reclaim.c */