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