From: Schanzenbach, Martin Date: Tue, 12 Mar 2019 08:36:17 +0000 (+0100) Subject: move reclaim and gns back into subdirs X-Git-Tag: v0.11.1~101 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=919bb8c92fa1b7cda53401ff2286b980ca0b12d8;p=oweals%2Fgnunet.git move reclaim and gns back into subdirs --- diff --git a/po/POTFILES.in b/po/POTFILES.in index 09e4c533d..7307f9c0a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -278,8 +278,12 @@ src/reclaim-attribute/plugin_reclaim_attribute_gnuid.c src/reclaim-attribute/reclaim_attribute.c src/reclaim/gnunet-reclaim.c src/reclaim/gnunet-service-reclaim.c +src/reclaim/json_reclaim.c +src/reclaim/oidc_helper.c src/reclaim/plugin_gnsrecord_reclaim.c src/reclaim/plugin_reclaim_sqlite.c +src/reclaim/plugin_rest_openid_connect.c +src/reclaim/plugin_rest_reclaim.c src/reclaim/reclaim_api.c src/regex/gnunet-daemon-regexprofiler.c src/regex/gnunet-regex-profiler.c @@ -295,16 +299,12 @@ src/regex/regex_internal_dht.c src/regex/regex_test_graph.c src/regex/regex_test_lib.c src/regex/regex_test_random.c -src/rest-plugins/json_reclaim.c -src/rest-plugins/oidc_helper.c src/rest-plugins/plugin_rest_copying.c src/rest-plugins/plugin_rest_credential.c src/rest-plugins/plugin_rest_gns.c src/rest-plugins/plugin_rest_identity.c src/rest-plugins/plugin_rest_namestore.c -src/rest-plugins/plugin_rest_openid_connect.c src/rest-plugins/plugin_rest_peerinfo.c -src/rest-plugins/plugin_rest_reclaim.c src/rest/gnunet-rest-server.c src/rest/rest.c src/revocation/gnunet-revocation.c diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am index 434d50f34..6ebbbcaff 100644 --- a/src/gns/Makefile.am +++ b/src/gns/Makefile.am @@ -89,9 +89,16 @@ bin_PROGRAMS += gnunet-bcd endif endif +if HAVE_MHD +if HAVE_JSON +REST_PLUGIN = libgnunet_plugin_rest_gns.la +endif +endif + plugin_LTLIBRARIES = \ libgnunet_plugin_block_gns.la \ - libgnunet_plugin_gnsrecord_gns.la + libgnunet_plugin_gnsrecord_gns.la \ + $(REST_PLUGIN) bin_SCRIPTS = \ @@ -104,6 +111,21 @@ gnunet-gns-proxy-setup-ca: gnunet-gns-proxy-setup-ca.in Makefile $(do_subst) < $(srcdir)/gnunet-gns-proxy-setup-ca.in > gnunet-gns-proxy-setup-ca chmod +x gnunet-gns-proxy-setup-ca + +libgnunet_plugin_rest_gns_la_SOURCES = \ + plugin_rest_gns.c +libgnunet_plugin_rest_gns_la_LIBADD = \ + $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ + libgnunetgns.la \ + $(top_builddir)/src/rest/libgnunetrest.la \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + $(top_builddir)/src/json/libgnunetjson.la \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson -lmicrohttpd +libgnunet_plugin_rest_gns_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + libgnunet_plugin_gnsrecord_gns_la_SOURCES = \ plugin_gnsrecord_gns.c libgnunet_plugin_gnsrecord_gns_la_LIBADD = \ diff --git a/src/gns/plugin_rest_gns.c b/src/gns/plugin_rest_gns.c new file mode 100644 index 000000000..e41df4085 --- /dev/null +++ b/src/gns/plugin_rest_gns.c @@ -0,0 +1,478 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Philippe Buschmann + * @file gns/plugin_rest_gns.c + * @brief GNUnet Gns REST plugin + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_rest_lib.h" +#include "gnunet_json_lib.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_gns_service.h" +#include "microhttpd.h" +#include + +/** + * Rest API GNS Namespace + */ +#define GNUNET_REST_API_NS_GNS "/gns" + +/** + * Rest API GNS Parameter record_type + */ +#define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type" + +/** + * Rest API GNS ERROR Unknown Error + */ +#define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error" + +/** + * Rest API GNS ERROR Record not found + */ +#define GNUNET_REST_GNS_NOT_FOUND "Record not found" + +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * The request handle + */ +struct RequestHandle +{ + + /** + * Connection to GNS + */ + struct GNUNET_GNS_Handle *gns; + + /** + * Active GNS lookup + */ + struct GNUNET_GNS_LookupWithTldRequest *gns_lookup; + + /** + * Name to look up + */ + char *name; + + /** + * Record type to look up + */ + int record_type; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Error response message + */ + char *emsg; + + /** + * Response code + */ + int response_code; + +}; + + +/** + * Cleanup lookup handle + * @param handle Handle to clean up + */ +static void +cleanup_handle (void *cls) +{ + struct RequestHandle *handle = cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + + if (NULL != handle->gns_lookup) + { + GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup); + handle->gns_lookup = NULL; + } + if (NULL != handle->gns) + { + GNUNET_GNS_disconnect (handle->gns); + handle->gns = NULL; + } + + if (NULL != handle->timeout_task) + { + GNUNET_SCHEDULER_cancel (handle->timeout_task); + handle->timeout_task = NULL; + } + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->name) + GNUNET_free (handle->name); + if (NULL != handle->emsg) + GNUNET_free (handle->emsg); + + GNUNET_free (handle); +} + + +/** + * Task run on errors. Reports an error and cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + json_t *json_error = json_object(); + char *response; + + if (NULL == handle->emsg) + handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN); + + json_object_set_new(json_error,"error", json_string(handle->emsg)); + + if (0 == handle->response_code) + handle->response_code = MHD_HTTP_OK; + response = json_dumps (json_error, 0); + resp = GNUNET_REST_create_response (response); + handle->proc (handle->proc_cls, resp, handle->response_code); + json_decref(json_error); + GNUNET_free(response); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +/** + * Iterator called on obtained result for a GNS lookup. + * + * @param cls closure with the object + * @param was_gns #GNUNET_NO if name was not a GNS name + * @param rd_count number of records in @a rd + * @param rd the records in reply + */ +static void +handle_gns_response (void *cls, + int was_gns, + uint32_t rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + json_t *result_array; + json_t *record_obj; + char *result; + + handle->gns_lookup = NULL; + + if (GNUNET_NO == was_gns) + { + handle->response_code = MHD_HTTP_NOT_FOUND; + handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + result_array = json_array(); + for (uint32_t i=0;irecord_type) && + (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) ) + { + continue; + } + + record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]); + json_array_append (result_array, record_obj); + json_decref (record_obj); + } + + result = json_dumps(result_array, 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result); + resp = GNUNET_REST_create_response (result); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (result); + json_decref (result_array); + GNUNET_SCHEDULER_add_now(&cleanup_handle, handle); +} + + +/** + * Handle gns GET request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_HashCode key; + char *record_type; + char *name; + + name = NULL; + handle->name = NULL; + if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url)) + { + name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1]; + } + + if (NULL == name) + { + handle->response_code = MHD_HTTP_NOT_FOUND; + handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (0 >= strlen (name)) + { + handle->response_code = MHD_HTTP_NOT_FOUND; + handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->name = GNUNET_strdup(name); + + handle->record_type = UINT32_MAX; + GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE, + strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE), + &key); + if ( GNUNET_YES + == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, + &key)) + { + record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key); + handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type); + } + + if(UINT32_MAX == handle->record_type) + { + handle->record_type = GNUNET_GNSRECORD_TYPE_ANY; + } + + handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns, + handle->name, + handle->record_type, + GNUNET_NO, + &handle_gns_response, + handle); +} + + + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_SCHEDULER_add_now(&cleanup_handle, handle); + return; +} + + +/** + * Handle rest request + * + * @param handle the request handle + */ +static void +init_cont (struct RequestHandle *handle) +{ + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont}, + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont}, + GNUNET_REST_HANDLER_END + }; + + if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } +} + + +/** + * Function processing the REST call + * + * @param method HTTP method + * @param url URL of the HTTP request + * @param data body of the HTTP request (optional) + * @param data_size length of the body + * @param proc callback function for the result + * @param proc_cls closure for callback function + * @return GNUNET_OK if request accepted + */ +static void +rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + + handle->response_code = 0; + handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60); + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->rest_handle = rest_handle; + + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); + handle->gns = GNUNET_GNS_connect (cfg); + init_cont(handle); + + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_error, + handle); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); +} + + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_gns_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_GNS; + api->process_request = &rest_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Gns REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_gns_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + plugin->cfg = NULL; + + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Gns REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_gns.c */ + diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index f9942fa23..fd488e1b9 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -16,6 +16,12 @@ if HAVE_SQLITE SQLITE_PLUGIN = libgnunet_plugin_reclaim_sqlite.la endif +if HAVE_JSON +REST_PLUGIN = \ + libgnunet_plugin_rest_openid_connect.la \ + libgnunet_plugin_rest_reclaim.la +endif + EXTRA_DIST = \ reclaim.conf \ test_reclaim_defaults.conf \ @@ -33,7 +39,8 @@ lib_LTLIBRARIES = \ libgnunetreclaim.la plugin_LTLIBRARIES = \ libgnunet_plugin_gnsrecord_reclaim.la \ - $(SQLITE_PLUGIN) + $(SQLITE_PLUGIN) \ + $(REST_PLUGIN) bin_PROGRAMS = \ gnunet-reclaim @@ -41,6 +48,41 @@ bin_PROGRAMS = \ libexec_PROGRAMS = \ gnunet-service-reclaim +libgnunet_plugin_rest_reclaim_la_SOURCES = \ + plugin_rest_reclaim.c \ + json_reclaim.h \ + json_reclaim.c +libgnunet_plugin_rest_reclaim_la_LIBADD = \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + libgnunetreclaim.la \ + $(top_builddir)/src/json/libgnunetjson.la \ + $(top_builddir)/src/rest/libgnunetrest.la \ + $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \ + $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson -lmicrohttpd +libgnunet_plugin_rest_reclaim_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + +libgnunet_plugin_rest_openid_connect_la_SOURCES = \ + plugin_rest_openid_connect.c \ + oidc_helper.h \ + oidc_helper.c +libgnunet_plugin_rest_openid_connect_la_LIBADD = \ + $(top_builddir)/src/identity/libgnunetidentity.la \ + libgnunetreclaim.la \ + $(top_builddir)/src/rest/libgnunetrest.la \ + $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \ + $(top_builddir)/src/namestore/libgnunetnamestore.la \ +$(top_builddir)/src/gns/libgnunetgns.la \ + $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson -lmicrohttpd +libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + libgnunet_plugin_gnsrecord_reclaim_la_SOURCES = \ plugin_gnsrecord_reclaim.c libgnunet_plugin_gnsrecord_reclaim_la_LIBADD = \ diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c new file mode 100644 index 000000000..0fe9150d9 --- /dev/null +++ b/src/reclaim/json_reclaim.c @@ -0,0 +1,242 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later +*/ + +/** + * @file rest-plugins/json_reclaim.c + * @brief JSON handling of reclaim data + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_json_lib.h" +#include "gnunet_reclaim_service.h" +#include "gnunet_reclaim_attribute_lib.h" + +/** + * Parse given JSON object to a claim + * + * @param cls closure, NULL + * @param root the json object representing data + * @param spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_attr (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr; + const char* name_str; + const char* val_str; + const char* type_str; + char *data; + int unpack_state; + uint32_t type; + size_t data_size; + + GNUNET_assert(NULL != root); + + if(!json_is_object(root)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error json is not array nor object!\n"); + return GNUNET_SYSERR; + } + //interpret single attribute + unpack_state = json_unpack(root, + "{s:s, s:s, s:s!}", + "name", &name_str, + "type", &type_str, + "value", &val_str); + if (0 != unpack_state) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Error json object has a wrong format!\n"); + return GNUNET_SYSERR; + } + type = GNUNET_RECLAIM_ATTRIBUTE_typename_to_number (type_str); + if (GNUNET_SYSERR == (GNUNET_RECLAIM_ATTRIBUTE_string_to_value (type, + val_str, + (void**)&data, + &data_size))) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Attribute value invalid!\n"); + return GNUNET_SYSERR; + } + attr = GNUNET_RECLAIM_ATTRIBUTE_claim_new (name_str, + type, + data, + data_size); + *(struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr = attr; + return GNUNET_OK; +} + +/** + * Cleanup data left from parsing RSA public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_attr (void *cls, struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr; + attr = (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr; + if (NULL != *attr) + { + GNUNET_free(*attr); + *attr = NULL; + } +} + +/** + * JSON Specification for Reclaim claims. + * + * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill + * @return JSON Specification + */ +struct GNUNET_JSON_Specification +GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_attr, + .cleaner = &clean_attr, + .cls = NULL, + .field = NULL, + .ptr = attr, + .ptr_size = 0, + .size_ptr = NULL + }; + *attr = NULL; + return ret; +} +/** + * Parse given JSON object to a ticket + * + * @param cls closure, NULL + * @param root the json object representing data + * @param spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_ticket (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_RECLAIM_Ticket *ticket; + const char* rnd_str; + const char* aud_str; + const char* id_str; + int unpack_state; + + GNUNET_assert(NULL != root); + + if(!json_is_object(root)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error json is not array nor object!\n"); + return GNUNET_SYSERR; + } + //interpret single ticket + unpack_state = json_unpack(root, + "{s:s, s:s, s:s!}", + "rnd", &rnd_str, + "audience", &aud_str, + "identity", &id_str); + if (0 != unpack_state) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Error json object has a wrong format!\n"); + return GNUNET_SYSERR; + } + ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (rnd_str, + strlen (rnd_str), + &ticket->rnd, + sizeof (uint64_t))) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Rnd invalid\n"); + GNUNET_free(ticket); + return GNUNET_SYSERR; + } + GNUNET_STRINGS_string_to_data (id_str, + strlen (id_str), + &ticket->identity, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Identity invalid\n"); + GNUNET_free(ticket); + return GNUNET_SYSERR; + } + + GNUNET_STRINGS_string_to_data (aud_str, + strlen (aud_str), + &ticket->audience, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Audience invalid\n"); + GNUNET_free(ticket); + return GNUNET_SYSERR; + } + + *(struct GNUNET_RECLAIM_Ticket **) spec->ptr = ticket; + return GNUNET_OK; +} + +/** + * Cleanup data left from parsing RSA public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_ticket (void *cls, struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_RECLAIM_Ticket **ticket; + ticket = (struct GNUNET_RECLAIM_Ticket **) spec->ptr; + if (NULL != *ticket) + { + GNUNET_free(*ticket); + *ticket = NULL; + } +} + +/** + * JSON Specification for Reclaim tickets. + * + * @param ticket struct of GNUNET_RECLAIM_Ticket to fill + * @return JSON Specification + */ +struct GNUNET_JSON_Specification +GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_ticket, + .cleaner = &clean_ticket, + .cls = NULL, + .field = NULL, + .ptr = ticket, + .ptr_size = 0, + .size_ptr = NULL + }; + *ticket = NULL; + return ret; +} diff --git a/src/reclaim/json_reclaim.h b/src/reclaim/json_reclaim.h new file mode 100644 index 000000000..ced2e10dd --- /dev/null +++ b/src/reclaim/json_reclaim.h @@ -0,0 +1,48 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later +*/ + +/** + * @file rest-plugins/json_reclaim.h + * @brief JSON handling of reclaim data + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_json_lib.h" +#include "gnunet_reclaim_service.h" +#include "gnunet_reclaim_attribute_lib.h" + +/** + * JSON Specification for Reclaim claims. + * + * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill + * @return JSON Specification + */ +struct GNUNET_JSON_Specification +GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr); + +/** + * JSON Specification for Reclaim tickets. + * + * @param ticket struct of GNUNET_RECLAIM_Ticket to fill + * @return JSON Specification + */ +struct GNUNET_JSON_Specification +GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket); diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c new file mode 100644 index 000000000..646e58551 --- /dev/null +++ b/src/reclaim/oidc_helper.c @@ -0,0 +1,436 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file reclaim/oidc_helper.c + * @brief helper library for OIDC related functions + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_signatures.h" +#include "gnunet_reclaim_service.h" +#include "gnunet_reclaim_attribute_lib.h" +#include +#include +#include "oidc_helper.h" + +static char* +create_jwt_header(void) +{ + json_t *root; + char *json_str; + + root = json_object (); + json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE)); + json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE)); + + json_str = json_dumps (root, JSON_INDENT(0) | JSON_COMPACT); + json_decref (root); + return json_str; +} + +static void +replace_char(char* str, char find, char replace){ + char *current_pos = strchr(str,find); + while (current_pos){ + *current_pos = replace; + current_pos = strchr(current_pos,find); + } +} + +//RFC4648 +static void +fix_base64(char* str) { + //Replace + with - + replace_char (str, '+', '-'); + + //Replace / with _ + replace_char (str, '/', '_'); + +} + +/** + * Create a JWT from attributes + * + * @param aud_key the public of the audience + * @param sub_key the public key of the subject + * @param attrs the attribute list + * @param expiration_time the validity of the token + * @param secret_key the key used to sign the JWT + * @return a new base64-encoded JWT string. + */ +char* +OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, + const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, + const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, + const struct GNUNET_TIME_Relative *expiration_time, + const char *nonce, + const char *secret_key) +{ + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; + struct GNUNET_HashCode signature; + struct GNUNET_TIME_Absolute exp_time; + struct GNUNET_TIME_Absolute time_now; + char* audience; + char* subject; + char* header; + char* body_str; + char* result; + char* header_base64; + char* body_base64; + char* signature_target; + char* signature_base64; + char* attr_val_str; + json_t* body; + + //iat REQUIRED time now + time_now = GNUNET_TIME_absolute_get(); + //exp REQUIRED time expired from config + exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time); + //auth_time only if max_age + //nonce only if nonce + // OPTIONAL acr,amr,azp + subject = GNUNET_STRINGS_data_to_string_alloc (sub_key, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + audience = GNUNET_STRINGS_data_to_string_alloc (aud_key, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + header = create_jwt_header (); + body = json_object (); + + //iss REQUIRED case sensitive server uri with https + //The issuer is the local reclaim instance (e.g. https://reclaim.id/api/openid) + json_object_set_new (body, + "iss", json_string (SERVER_ADDRESS)); + //sub REQUIRED public key identity, not exceed 255 ASCII length + json_object_set_new (body, + "sub", json_string (subject)); + //aud REQUIRED public key client_id must be there + json_object_set_new (body, + "aud", json_string (audience)); + //iat + json_object_set_new (body, + "iat", json_integer (time_now.abs_value_us / (1000*1000))); + //exp + json_object_set_new (body, + "exp", json_integer (exp_time.abs_value_us / (1000*1000))); + //nbf + json_object_set_new (body, + "nbf", json_integer (time_now.abs_value_us / (1000*1000))); + //nonce + if (NULL != nonce) + json_object_set_new (body, + "nonce", json_string (nonce)); + + for (le = attrs->list_head; NULL != le; le = le->next) + { + attr_val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type, + le->claim->data, + le->claim->data_size); + json_object_set_new (body, + le->claim->name, + json_string (attr_val_str)); + GNUNET_free (attr_val_str); + } + body_str = json_dumps (body, JSON_INDENT(0) | JSON_COMPACT); + json_decref (body); + + GNUNET_STRINGS_base64_encode (header, + strlen (header), + &header_base64); + fix_base64(header_base64); + + GNUNET_STRINGS_base64_encode (body_str, + strlen (body_str), + &body_base64); + fix_base64(body_base64); + + GNUNET_free (subject); + GNUNET_free (audience); + + /** + * Creating the JWT signature. This might not be + * standards compliant, check. + */ + GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64); + GNUNET_CRYPTO_hmac_raw (secret_key, strlen (secret_key), signature_target, strlen (signature_target), &signature); + GNUNET_STRINGS_base64_encode ((const char*)&signature, + sizeof (struct GNUNET_HashCode), + &signature_base64); + fix_base64(signature_base64); + + GNUNET_asprintf (&result, "%s.%s.%s", + header_base64, body_base64, signature_base64); + + GNUNET_free (signature_target); + GNUNET_free (header); + GNUNET_free (body_str); + GNUNET_free (signature_base64); + GNUNET_free (body_base64); + GNUNET_free (header_base64); + return result; +} +/** + * Builds an OIDC authorization code including + * a reclaim ticket and nonce + * + * @param issuer the issuer of the ticket, used to sign the ticket and nonce + * @param ticket the ticket to include in the code + * @param nonce the nonce to include in the code + * @return a new authorization code (caller must free) + */ +char* +OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, + const struct GNUNET_RECLAIM_Ticket *ticket, + const char* nonce) +{ + char *ticket_str; + json_t *code_json; + char *signature_payload; + char *signature_str; + char *authz_code; + size_t signature_payload_len; + struct GNUNET_CRYPTO_EcdsaSignature signature; + struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; + + signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket); + if (NULL != nonce) + signature_payload_len += strlen (nonce); + + signature_payload = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); + purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *)signature_payload; + purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); + purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); + memcpy (&purpose[1], + ticket, + sizeof (struct GNUNET_RECLAIM_Ticket)); + if (NULL != nonce) + memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket), + nonce, + strlen (nonce)); + if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_sign (issuer, + purpose, + &signature)) + { + GNUNET_free (signature_payload); + return NULL; + } + signature_str = GNUNET_STRINGS_data_to_string_alloc (&signature, + sizeof (signature)); + ticket_str = GNUNET_STRINGS_data_to_string_alloc (ticket, + sizeof (struct GNUNET_RECLAIM_Ticket)); + + code_json = json_object (); + json_object_set_new (code_json, + "ticket", + json_string (ticket_str)); + if (NULL != nonce) + json_object_set_new (code_json, + "nonce", + json_string (nonce)); + json_object_set_new (code_json, + "signature", + json_string (signature_str)); + authz_code = json_dumps (code_json, + JSON_INDENT(0) | JSON_COMPACT); + GNUNET_free (signature_payload); + GNUNET_free (signature_str); + GNUNET_free (ticket_str); + json_decref (code_json); + return authz_code; +} + + + + +/** + * Parse reclaim ticket and nonce from + * authorization code. + * This also verifies the signature in the code. + * + * @param audience the expected audience of the code + * @param code the string representation of the code + * @param ticket where to store the ticket + * @param nonce where to store the nonce + * @return GNUNET_OK if successful, else GNUNET_SYSERR + */ +int +OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, + const char* code, + struct GNUNET_RECLAIM_Ticket **ticket, + char **nonce) +{ + json_error_t error; + json_t *code_json; + json_t *ticket_json; + json_t *nonce_json; + json_t *signature_json; + const char *ticket_str; + const char *signature_str; + const char *nonce_str; + char *code_output; + struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; + struct GNUNET_CRYPTO_EcdsaSignature signature; + size_t signature_payload_len; + + code_output = NULL; + GNUNET_STRINGS_base64_decode (code, + strlen(code), + (void**)&code_output); + code_json = json_loads (code_output, 0 , &error); + GNUNET_free (code_output); + ticket_json = json_object_get (code_json, "ticket"); + nonce_json = json_object_get (code_json, "nonce"); + signature_json = json_object_get (code_json, "signature"); + *ticket = NULL; + *nonce = NULL; + + if ((NULL == ticket_json || !json_is_string (ticket_json)) || + (NULL == signature_json || !json_is_string (signature_json))) + { + json_decref (code_json); + return GNUNET_SYSERR; + } + ticket_str = json_string_value (ticket_json); + signature_str = json_string_value (signature_json); + nonce_str = NULL; + if (NULL != nonce_json) + nonce_str = json_string_value (nonce_json); + signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket); + if (NULL != nonce_str) + signature_payload_len += strlen (nonce_str); + purpose = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + + signature_payload_len); + purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); + purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (ticket_str, + strlen (ticket_str), + &purpose[1], + sizeof (struct GNUNET_RECLAIM_Ticket))) + { + GNUNET_free (purpose); + json_decref (code_json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cannot parse ticket!\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (signature_str, + strlen (signature_str), + &signature, + sizeof (struct GNUNET_CRYPTO_EcdsaSignature))) + { + GNUNET_free (purpose); + json_decref (code_json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cannot parse signature!\n"); + return GNUNET_SYSERR; + } + *ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); + memcpy (*ticket, + &purpose[1], + sizeof (struct GNUNET_RECLAIM_Ticket)); + if (0 != memcmp (audience, + &(*ticket)->audience, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + { + GNUNET_free (purpose); + GNUNET_free (*ticket); + json_decref (code_json); + *ticket = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Audience in ticket does not match client!\n"); + return GNUNET_SYSERR; + + } + if (NULL != nonce_str) + memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket), + nonce_str, + strlen (nonce_str)); + if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN, + purpose, + &signature, + &(*ticket)->identity)) + { + GNUNET_free (purpose); + GNUNET_free (*ticket); + json_decref (code_json); + *ticket = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Signature of authZ code invalid!\n"); + return GNUNET_SYSERR; + } + *nonce = GNUNET_strdup (nonce_str); + return GNUNET_OK; +} + +/** + * Build a token response for a token request + * TODO: Maybe we should add the scope here? + * + * @param access_token the access token to include + * @param id_token the id_token to include + * @param expiration_time the expiration time of the token(s) + * @param token_response where to store the response + */ +void +OIDC_build_token_response (const char *access_token, + const char *id_token, + const struct GNUNET_TIME_Relative *expiration_time, + char **token_response) +{ + json_t *root_json; + + root_json = json_object (); + + GNUNET_assert (NULL != access_token); + GNUNET_assert (NULL != id_token); + GNUNET_assert (NULL != expiration_time); + json_object_set_new (root_json, + "access_token", + json_string (access_token)); + json_object_set_new (root_json, + "token_type", + json_string ("Bearer")); + json_object_set_new (root_json, + "expires_in", + json_integer (expiration_time->rel_value_us / (1000 * 1000))); + json_object_set_new (root_json, + "id_token", + json_string (id_token)); + *token_response = json_dumps (root_json, + JSON_INDENT(0) | JSON_COMPACT); + json_decref (root_json); +} + +/** + * Generate a new access token + */ +char* +OIDC_access_token_new () +{ + char* access_token_number; + char* access_token; + uint64_t random_number; + + random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + GNUNET_asprintf (&access_token_number, "%" PRIu64, random_number); + GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token); + return access_token; +} diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h new file mode 100644 index 000000000..d718b7a78 --- /dev/null +++ b/src/reclaim/oidc_helper.h @@ -0,0 +1,111 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file reclaim/oidc_helper.h + * @brief helper library for OIDC related functions + * @author Martin Schanzenbach + */ + +#ifndef JWT_H +#define JWT_H + +#define JWT_ALG "alg" + +/* Use 512bit HMAC */ +#define JWT_ALG_VALUE "HS512" + +#define JWT_TYP "typ" + +#define JWT_TYP_VALUE "jwt" + +#define SERVER_ADDRESS "https://api.reclaim" + +/** + * Create a JWT from attributes + * + * @param aud_key the public of the audience + * @param sub_key the public key of the subject + * @param attrs the attribute list + * @param expiration_time the validity of the token + * @param secret_key the key used to sign the JWT + * @return a new base64-encoded JWT string. + */ +char* +OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, + const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, + const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, + const struct GNUNET_TIME_Relative *expiration_time, + const char *nonce, + const char *secret_key); + +/** + * Builds an OIDC authorization code including + * a reclaim ticket and nonce + * + * @param issuer the issuer of the ticket, used to sign the ticket and nonce + * @param ticket the ticket to include in the code + * @param nonce the nonce to include in the code + * @return a new authorization code (caller must free) + */ +char* +OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, + const struct GNUNET_RECLAIM_Ticket *ticket, + const char* nonce); + +/** + * Parse reclaim ticket and nonce from + * authorization code. + * This also verifies the signature in the code. + * + * @param audience the expected audience of the code + * @param code the string representation of the code + * @param ticket where to store the ticket + * @param nonce where to store the nonce + * @return GNUNET_OK if successful, else GNUNET_SYSERR + */ +int +OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, + const char* code, + struct GNUNET_RECLAIM_Ticket **ticket, + char **nonce); + +/** + * Build a token response for a token request + * TODO: Maybe we should add the scope here? + * + * @param access_token the access token to include + * @param id_token the id_token to include + * @param expiration_time the expiration time of the token(s) + * @param token_response where to store the response + */ +void +OIDC_build_token_response (const char *access_token, + const char *id_token, + const struct GNUNET_TIME_Relative *expiration_time, + char **token_response); +/** + * Generate a new access token + */ +char* +OIDC_access_token_new (); + + +#endif diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c new file mode 100644 index 000000000..e755f079b --- /dev/null +++ b/src/reclaim/plugin_rest_openid_connect.c @@ -0,0 +1,2330 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Martin Schanzenbach + * @author Philippe Buschmann + * @file identity/plugin_rest_openid_connect.c + * @brief GNUnet Namestore REST plugin + * + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_identity_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_namestore_service.h" +#include "gnunet_rest_lib.h" +#include "microhttpd.h" +#include +#include +#include "gnunet_signatures.h" +#include "gnunet_reclaim_attribute_lib.h" +#include "gnunet_reclaim_service.h" +#include "oidc_helper.h" + +/** + * REST root namespace + */ +#define GNUNET_REST_API_NS_OIDC "/openid" + +/** + * Authorize endpoint + */ +#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize" + +/** + * Token endpoint + */ +#define GNUNET_REST_API_NS_TOKEN "/openid/token" + +/** + * UserInfo endpoint + */ +#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo" + +/** + * Login namespace + */ +#define GNUNET_REST_API_NS_LOGIN "/openid/login" + +/** + * State while collecting all egos + */ +#define ID_REST_STATE_INIT 0 + +/** + * Done collecting egos + */ +#define ID_REST_STATE_POST_INIT 1 + +/** + * OIDC grant_type key + */ +#define OIDC_GRANT_TYPE_KEY "grant_type" + +/** + * OIDC grant_type key + */ +#define OIDC_GRANT_TYPE_VALUE "authorization_code" + +/** + * OIDC code key + */ +#define OIDC_CODE_KEY "code" + +/** + * OIDC response_type key + */ +#define OIDC_RESPONSE_TYPE_KEY "response_type" + +/** + * OIDC client_id key + */ +#define OIDC_CLIENT_ID_KEY "client_id" + +/** + * OIDC scope key + */ +#define OIDC_SCOPE_KEY "scope" + +/** + * OIDC redirect_uri key + */ +#define OIDC_REDIRECT_URI_KEY "redirect_uri" + +/** + * OIDC state key + */ +#define OIDC_STATE_KEY "state" + +/** + * OIDC nonce key + */ +#define OIDC_NONCE_KEY "nonce" + +/** + * OIDC cookie expiration (in seconds) + */ +#define OIDC_COOKIE_EXPIRATION 3 + +/** + * OIDC cookie header key + */ +#define OIDC_COOKIE_HEADER_KEY "cookie" + +/** + * OIDC cookie header information key + */ +#define OIDC_AUTHORIZATION_HEADER_KEY "authorization" + +/** + * OIDC cookie header information key + */ +#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity=" + +/** + * OIDC cookie header if user cancelled + */ +#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied" + +/** + * OIDC expected response_type while authorizing + */ +#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code" + +/** + * OIDC expected scope part while authorizing + */ +#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid" + +/** + * OIDC error key for invalid client + */ +#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client" + +/** + * OIDC error key for invalid scopes + */ +#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope" + +/** + * OIDC error key for invalid requests + */ +#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request" + +/** + * OIDC error key for invalid tokens + */ +#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token" + +/** + * OIDC error key for invalid cookies + */ +#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie" + +/** + * OIDC error key for generic server errors + */ +#define OIDC_ERROR_KEY_SERVER_ERROR "server_error" + +/** + * OIDC error key for unsupported grants + */ +#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type" + +/** + * OIDC error key for unsupported response types + */ +#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type" + +/** + * OIDC error key for unauthorized clients + */ +#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client" + +/** + * OIDC error key for denied access + */ +#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" + + + +/** + * OIDC ignored parameter array + */ +static char* OIDC_ignored_parameter_array [] = +{ + "display", + "prompt", + "ui_locales", + "response_mode", + "id_token_hint", + "login_hint", + "acr_values" +}; + +/** + * OIDC Hash map that keeps track of issued cookies + */ +struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map; + +/** + * OIDC authorized identities and times hashmap + */ +struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants; + +/** + * OIDC Hash map that keeps track of used authorization code(s) + */ +struct GNUNET_CONTAINER_MultiHashMap *OIDC_used_ticket_map; + +/** + * Hash map that links the issued access token to the corresponding ticket and ego + */ +struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map; + +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * OIDC needed variables + */ +struct OIDC_Variables +{ + /** + * The RP client public key + */ + struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey; + + /** + * The OIDC client id of the RP + */ + char *client_id; + + /** + * The OIDC redirect uri + */ + char *redirect_uri; + + /** + * The list of oidc scopes + */ + char *scope; + + /** + * The OIDC state + */ + char *state; + + /** + * The OIDC nonce + */ + char *nonce; + + /** + * The OIDC response type + */ + char *response_type; + + /** + * The identity chosen by the user to login + */ + char *login_identity; + + /** + * User cancelled authorization/login + */ + int user_cancelled; + + /** + * The response JSON + */ + json_t *response; + +}; + +/** + * The ego list + */ +struct EgoEntry +{ + /** + * DLL + */ + struct EgoEntry *next; + + /** + * DLL + */ + struct EgoEntry *prev; + + /** + * Ego Identifier + */ + char *identifier; + + /** + * Public key string + */ + char *keystring; + + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; +}; + + +struct RequestHandle +{ + /** + * Ego list + */ + struct EgoEntry *ego_head; + + /** + * Ego list + */ + struct EgoEntry *ego_tail; + + /** + * Selected ego + */ + struct EgoEntry *ego_entry; + + /** + * Pointer to ego private key + */ + struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key; + + /** + * OIDC variables + */ + struct OIDC_Variables *oidc; + + /** + * The processing state + */ + int state; + + /** + * Handle to Identity service. + */ + struct GNUNET_IDENTITY_Handle *identity_handle; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * GNS handle + */ + struct GNUNET_GNS_Handle *gns_handle; + + /** + * GNS lookup op + */ + struct GNUNET_GNS_LookupRequest *gns_op; + + /** + * Handle to NAMESTORE + */ + struct GNUNET_NAMESTORE_Handle *namestore_handle; + + /** + * Iterator for NAMESTORE + */ + struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it; + + /** + * Attribute claim list + */ + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list; + + /** + * IDENTITY Operation + */ + struct GNUNET_IDENTITY_Operation *op; + + /** + * Identity Provider + */ + struct GNUNET_RECLAIM_Handle *idp; + + /** + * Idp Operation + */ + struct GNUNET_RECLAIM_Operation *idp_op; + + /** + * Attribute iterator + */ + struct GNUNET_RECLAIM_AttributeIterator *attr_it; + + /** + * Ticket iterator + */ + struct GNUNET_RECLAIM_TicketIterator *ticket_it; + + /** + * A ticket + */ + struct GNUNET_RECLAIM_Ticket ticket; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * The tld for redirect + */ + char *tld; + + /** + * The redirect prefix + */ + char *redirect_prefix; + + /** + * The redirect suffix + */ + char *redirect_suffix; + + /** + * Error response message + */ + char *emsg; + + /** + * Error response description + */ + char *edesc; + + /** + * Reponse code + */ + int response_code; + +}; + +/** + * Cleanup lookup handle + * @param handle Handle to clean up + */ +static void +cleanup_handle (struct RequestHandle *handle) +{ + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry; + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp; + struct EgoEntry *ego_entry; + struct EgoEntry *ego_tmp; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel (handle->timeout_task); + if (NULL != handle->identity_handle) + GNUNET_IDENTITY_disconnect (handle->identity_handle); + if (NULL != handle->attr_it) + GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); + if (NULL != handle->ticket_it) + GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); + if (NULL != handle->idp) + GNUNET_RECLAIM_disconnect (handle->idp); + GNUNET_free_non_null (handle->url); + GNUNET_free_non_null (handle->tld); + GNUNET_free_non_null (handle->redirect_prefix); + GNUNET_free_non_null (handle->redirect_suffix); + GNUNET_free_non_null (handle->emsg); + GNUNET_free_non_null (handle->edesc); + if (NULL != handle->gns_op) + GNUNET_GNS_lookup_cancel (handle->gns_op); + if (NULL != handle->gns_handle) + GNUNET_GNS_disconnect (handle->gns_handle); + + if (NULL != handle->namestore_handle) + GNUNET_NAMESTORE_disconnect (handle->namestore_handle); + if (NULL != handle->oidc) + { + GNUNET_free_non_null (handle->oidc->client_id); + GNUNET_free_non_null (handle->oidc->login_identity); + GNUNET_free_non_null (handle->oidc->nonce); + GNUNET_free_non_null (handle->oidc->redirect_uri); + GNUNET_free_non_null (handle->oidc->response_type); + GNUNET_free_non_null (handle->oidc->scope); + GNUNET_free_non_null (handle->oidc->state); + json_decref (handle->oidc->response); + GNUNET_free (handle->oidc); + } + if ( NULL != handle->attr_list ) + { + for (claim_entry = handle->attr_list->list_head; + NULL != claim_entry;) + { + claim_tmp = claim_entry; + claim_entry = claim_entry->next; + GNUNET_free (claim_tmp->claim); + GNUNET_free (claim_tmp); + } + GNUNET_free (handle->attr_list); + } + for (ego_entry = handle->ego_head; + NULL != ego_entry;) + { + ego_tmp = ego_entry; + ego_entry = ego_entry->next; + GNUNET_free (ego_tmp->identifier); + GNUNET_free (ego_tmp->keystring); + GNUNET_free (ego_tmp); + } + GNUNET_free_non_null (handle->attr_it); + GNUNET_free (handle); +} + +static void +cleanup_handle_delayed (void *cls) +{ + cleanup_handle (cls); +} + + +/** + * Task run on error, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *json_error; + + GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}", + handle->emsg, + (NULL != handle->edesc) ? handle->edesc : "", + (NULL != handle->oidc->state) ? ", \"state\":\"" : "", + (NULL != handle->oidc->state) ? handle->oidc->state : "", + (NULL != handle->oidc->state) ? "\"" : ""); + if ( 0 == handle->response_code ) + handle->response_code = MHD_HTTP_BAD_REQUEST; + resp = GNUNET_REST_create_response (json_error); + if (MHD_HTTP_UNAUTHORIZED == handle->response_code) + MHD_add_response_header (resp, + MHD_HTTP_HEADER_WWW_AUTHENTICATE, + "Basic"); + MHD_add_response_header (resp, + MHD_HTTP_HEADER_CONTENT_TYPE, + "application/json"); + handle->proc (handle->proc_cls, resp, handle->response_code); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (json_error); +} + + +/** + * Task run on error in userinfo endpoint, sends error header. Cleans up + * everything + * + * @param cls the `struct RequestHandle` + */ +static void +do_userinfo_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *error; + + GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"", + handle->emsg, + (NULL != handle->edesc) ? handle->edesc : ""); + resp = GNUNET_REST_create_response (""); + MHD_add_response_header (resp, + MHD_HTTP_HEADER_WWW_AUTHENTICATE, + "Bearer"); + handle->proc (handle->proc_cls, resp, handle->response_code); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (error); +} + + +/** + * Task run on error, sends error message and redirects. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_redirect_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char* redirect; + GNUNET_asprintf (&redirect, + "%s?error=%s&error_description=%s%s%s", + handle->oidc->redirect_uri, handle->emsg, handle->edesc, + (NULL != handle->oidc->state) ? "&state=" : "", + (NULL != handle->oidc->state) ? handle->oidc->state : ""); + resp = GNUNET_REST_create_response (""); + MHD_add_response_header (resp, "Location", redirect); + handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (redirect); +} + +/** + * Task run on timeout, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_timeout (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->timeout_task = NULL; + do_error (handle); +} + +/** + * Return attributes for claim + * + * @param cls the request handle + */ +static void +return_userinfo_response (void *cls) +{ + char* result_str; + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + result_str = json_dumps (handle->oidc->response, 0); + + resp = GNUNET_REST_create_response (result_str); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (result_str); + cleanup_handle (handle); +} + +/** + * Returns base64 encoded string urlencoded + * + * @param string the string to encode + * @return base64 encoded string + */ +static char* +base64_encode (const char *s) +{ + char *enc; + char *enc_urlencode; + char *tmp; + int i; + int num_pads = 0; + + GNUNET_STRINGS_base64_encode (s, strlen (s), &enc); + tmp = strchr (enc, '='); + num_pads = strlen (enc) - (tmp - enc); + GNUNET_assert ((3 > num_pads) && (0 <= num_pads)); + if (0 == num_pads) + return enc; + enc_urlencode = GNUNET_malloc (strlen (enc) + num_pads*2); + strcpy (enc_urlencode, enc); + GNUNET_free (enc); + tmp = strchr (enc_urlencode, '='); + for (i = 0; i < num_pads; i++) + { + strcpy (tmp, "%3D"); //replace '=' with '%3D' + tmp += 3; + } + return enc_urlencode; +} + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //For now, independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + cleanup_handle (handle); + return; +} + + +/** + * Interprets cookie header and pass its identity keystring to handle + */ +static void +cookie_identity_interpretation (struct RequestHandle *handle) +{ + struct GNUNET_HashCode cache_key; + char *cookies; + struct GNUNET_TIME_Absolute current_time, *relog_time; + char delimiter[] = "; "; + char *tmp_cookies; + char *token; + char *value; + + //gets identity of login try with cookie + GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY), + &cache_key); + if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, + &cache_key) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No cookie found\n"); + return; + } + //splits cookies and find 'Identity' cookie + tmp_cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key); + cookies = GNUNET_strdup (tmp_cookies); + token = strtok (cookies, delimiter); + handle->oidc->user_cancelled = GNUNET_NO; + handle->oidc->login_identity = NULL; + if (NULL == token) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse cookie: %s\n", + cookies); + GNUNET_free (cookies); + return; + } + + while (NULL != token) + { + if (0 == strcmp (token, + OIDC_COOKIE_HEADER_ACCESS_DENIED)) + { + handle->oidc->user_cancelled = GNUNET_YES; + GNUNET_free (cookies); + return; + } + if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY)) + break; + token = strtok (NULL, delimiter); + } + if (NULL == token) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No cookie value to process: %s\n", + cookies); + GNUNET_free (cookies); + return; + } + GNUNET_CRYPTO_hash (token, strlen (token), + &cache_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Found cookie `%s', but no corresponding expiration entry present...\n", + token); + GNUNET_free (cookies); + return; + } + relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, + &cache_key); + current_time = GNUNET_TIME_absolute_get (); + // 30 min after old login -> redirect to login + if ( current_time.abs_value_us > relog_time->abs_value_us ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Found cookie `%s', but it is expired.\n", + token); + GNUNET_free (cookies); + return; + } + value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY); + GNUNET_assert (NULL != value); + handle->oidc->login_identity = GNUNET_strdup (value); +} + +/** + * Redirects to login page stored in configuration file + */ +static void +login_redirect (void *cls) +{ + char *login_base_url; + char *new_redirect; + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + if ( GNUNET_OK + == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin", + "address", &login_base_url) ) + { + GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", + login_base_url, + OIDC_RESPONSE_TYPE_KEY, + handle->oidc->response_type, + OIDC_CLIENT_ID_KEY, + handle->oidc->client_id, + OIDC_REDIRECT_URI_KEY, + handle->oidc->redirect_uri, + OIDC_SCOPE_KEY, + handle->oidc->scope, + OIDC_STATE_KEY, + (NULL != handle->oidc->state) ? handle->oidc->state : "", + OIDC_NONCE_KEY, + (NULL != handle->oidc->nonce) ? handle->oidc->nonce : ""); + resp = GNUNET_REST_create_response (""); + MHD_add_response_header (resp, "Location", new_redirect); + GNUNET_free (login_base_url); + } + else + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("gnunet configuration failed"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); + GNUNET_free (new_redirect); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + +/** + * Does internal server error when iteration failed. + */ +static void +oidc_iteration_error (void *cls) +{ + struct RequestHandle *handle = cls; + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); +} + + +/** + * Issues ticket and redirects to relying party with the authorization code as + * parameter. Otherwise redirects with error + */ +static void +oidc_ticket_issue_cb (void* cls, + const struct GNUNET_RECLAIM_Ticket *ticket) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *ticket_str; + char *redirect_uri; + char *code_json_string; + char *code_base64_final_string; + + handle->idp_op = NULL; + handle->ticket = *ticket; + if (NULL == ticket) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("Server cannot generate ticket."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket, + sizeof (struct GNUNET_RECLAIM_Ticket)); + //TODO change if more attributes are needed (see max_age) + code_json_string = OIDC_build_authz_code (&handle->priv_key, + &handle->ticket, + handle->oidc->nonce); + code_base64_final_string = base64_encode (code_json_string); + if ( (NULL != handle->redirect_prefix) && + (NULL != handle->redirect_suffix) && + (NULL != handle->tld) ) + { + + GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s", + handle->redirect_prefix, + handle->tld, + handle->redirect_suffix, + handle->oidc->response_type, + code_base64_final_string, handle->oidc->state); + } else { + GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s", + handle->oidc->redirect_uri, + handle->oidc->response_type, + code_base64_final_string, handle->oidc->state); + + } + resp = GNUNET_REST_create_response (""); + MHD_add_response_header (resp, "Location", redirect_uri); + handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (redirect_uri); + GNUNET_free (ticket_str); + GNUNET_free (code_json_string); + GNUNET_free (code_base64_final_string); +} + +static void +oidc_collect_finished_cb (void *cls) +{ + struct RequestHandle *handle = cls; + handle->attr_it = NULL; + handle->ticket_it = NULL; + if (NULL == handle->attr_list->list_head) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); + handle->edesc = GNUNET_strdup ("The requested scope is not available."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp, + &handle->priv_key, + &handle->oidc->client_pkey, + handle->attr_list, + &oidc_ticket_issue_cb, + handle); +} + + +/** + * Collects all attributes for an ego if in scope parameter + */ +static void +oidc_attr_collect (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) +{ + struct RequestHandle *handle = cls; + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; + char* scope_variables; + char* scope_variable; + char delimiter[]=" "; + + if ( (NULL == attr->name) || (NULL == attr->data) ) + { + GNUNET_RECLAIM_get_attributes_next (handle->attr_it); + return; + } + + scope_variables = GNUNET_strdup (handle->oidc->scope); + scope_variable = strtok (scope_variables, delimiter); + while (NULL != scope_variable) + { + if ( 0 == strcmp (attr->name, scope_variable) ) + { + break; + } + scope_variable = strtok (NULL, delimiter); + } + if ( NULL == scope_variable ) + { + GNUNET_RECLAIM_get_attributes_next (handle->attr_it); + GNUNET_free (scope_variables); + return; + } + GNUNET_free (scope_variables); + + le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); + le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type, + attr->data, attr->data_size); + GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head, + handle->attr_list->list_tail, le); + GNUNET_RECLAIM_get_attributes_next (handle->attr_it); +} + + +/** + * Checks time and cookie and redirects accordingly + */ +static void +code_redirect (void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_TIME_Absolute current_time, *relog_time; + struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey; + struct GNUNET_HashCode cache_key; + char *identity_cookie; + + GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity); + GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key); + GNUNET_free (identity_cookie); + //No login time for identity -> redirect to login + if ( GNUNET_YES + == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, + &cache_key) ) + { + relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, + &cache_key); + current_time = GNUNET_TIME_absolute_get (); + // 30 min after old login -> redirect to login + if ( current_time.abs_value_us <= relog_time->abs_value_us ) + { + if ( GNUNET_OK + != GNUNET_CRYPTO_ecdsa_public_key_from_string ( + handle->oidc->login_identity, + strlen (handle->oidc->login_identity), &pubkey) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE); + handle->edesc = GNUNET_strdup ("The cookie of a login identity is not valid"); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + // iterate over egos and compare their public key + for (handle->ego_entry = handle->ego_head; + NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next) + { + GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey); + if ( 0 == memcmp (&ego_pkey, + &pubkey, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) ) + { + handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego); + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->attr_list = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); + handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp, + &handle->priv_key, + &oidc_iteration_error, + handle, + &oidc_attr_collect, + handle, + &oidc_collect_finished_cb, + handle); + return; + } + } + GNUNET_SCHEDULER_add_now (&login_redirect, handle); + return; + } + } +} + + +static void +build_redirect (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char* redirect_uri; + + if (GNUNET_YES == handle->oidc->user_cancelled) + { + if ( (NULL != handle->redirect_prefix) && + (NULL != handle->redirect_suffix) && + (NULL != handle->tld) ) + { + GNUNET_asprintf (&redirect_uri, "%s.%s/%s?error=%s&error_description=%s&state=%s", + handle->redirect_prefix, + handle->tld, + handle->redirect_suffix, + "access_denied", + "User denied access", + handle->oidc->state); + } else { + GNUNET_asprintf (&redirect_uri, "%s?error=%s&error_description=%s&state=%s", + handle->oidc->redirect_uri, + "access_denied", + "User denied access", + handle->oidc->state); + + } + resp = GNUNET_REST_create_response (""); + MHD_add_response_header (resp, "Location", redirect_uri); + handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (redirect_uri); + return; + } + GNUNET_SCHEDULER_add_now (&code_redirect, handle); +} + + +static void +lookup_redirect_uri_result (void *cls, + uint32_t rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + char *tmp; + char *tmp_key_str; + char *pos; + struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone; + + handle->gns_op = NULL; + if (0 == rd_count) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + for (int i = 0; i < rd_count; i++) + { + if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type) + continue; + if (0 != strncmp (rd[i].data, + handle->oidc->redirect_uri, + rd[i].data_size)) + continue; + tmp = GNUNET_strndup (rd[i].data, + rd[i].data_size); + if (NULL == strstr (tmp, + handle->oidc->client_id)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Redirect uri %s does not contain client_id %s", + tmp, + handle->oidc->client_id); + } else { + + pos = strrchr (tmp, + (unsigned char) '.'); + *pos = '\0'; + handle->redirect_prefix = GNUNET_strdup (tmp); + tmp_key_str = pos + 1; + pos = strchr (tmp_key_str, + (unsigned char) '/'); + *pos = '\0'; + handle->redirect_suffix = GNUNET_strdup (pos + 1); + + GNUNET_STRINGS_string_to_data (tmp_key_str, + strlen (tmp_key_str), + &redirect_zone, + sizeof (redirect_zone)); + } + GNUNET_SCHEDULER_add_now (&build_redirect, handle); + GNUNET_free (tmp); + return; + } + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); +} + + + +/** + * Initiate redirect back to client. + */ +static void +client_redirect (void *cls) +{ + struct RequestHandle *handle = cls; + + /* Lookup client redirect uri to verify request */ + handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle, + GNUNET_GNS_EMPTY_LABEL_AT, + &handle->oidc->client_pkey, + GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT, + GNUNET_GNS_LO_DEFAULT, + &lookup_redirect_uri_result, + handle); + +} + + +/** + * Iteration over all results finished, build final + * response. + * + * @param cls the `struct RequestHandle` + */ +static void +build_authz_response (void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_HashCode cache_key; + + char *expected_scope; + char delimiter[]=" "; + int number_of_ignored_parameter, iterator; + + + // REQUIRED value: redirect_uri + GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY), + &cache_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc=GNUNET_strdup ("missing parameter redirect_uri"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->oidc->redirect_uri = + GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key)); + + // REQUIRED value: response_type + GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY), + &cache_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc=GNUNET_strdup ("missing parameter response_type"); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type); + + // REQUIRED value: scope + GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); + handle->edesc=GNUNET_strdup ("missing parameter scope"); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + handle->oidc->scope = GNUNET_strdup (handle->oidc->scope); + + //OPTIONAL value: nonce + GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce); + } + + //TODO check other values if needed + number_of_ignored_parameter = sizeof (OIDC_ignored_parameter_array) / sizeof (char *); + for (iterator = 0; iterator < number_of_ignored_parameter; iterator++) + { + GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator], + strlen (OIDC_ignored_parameter_array[iterator]), + &cache_key); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED); + GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s", + OIDC_ignored_parameter_array[iterator]); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + } + + // Checks if response_type is 'code' + if (0 != strcmp (handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE); + handle->edesc=GNUNET_strdup ("The authorization server does not support " + "obtaining this authorization code."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + return; + } + + // Checks if scope contains 'openid' + expected_scope = GNUNET_strdup (handle->oidc->scope); + char* test; + test = strtok (expected_scope, delimiter); + while (NULL != test) + { + if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) ) + break; + test = strtok (NULL, delimiter); + } + if (NULL == test) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); + handle->edesc=GNUNET_strdup ("The requested scope is invalid, unknown, or " + "malformed."); + GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); + GNUNET_free (expected_scope); + return; + } + + GNUNET_free (expected_scope); + if ( (NULL == handle->oidc->login_identity) && + (GNUNET_NO == handle->oidc->user_cancelled)) + GNUNET_SCHEDULER_add_now (&login_redirect, handle); + else + GNUNET_SCHEDULER_add_now (&client_redirect, handle); +} + +/** + * Iterate over tlds in config + */ +static void +tld_iter (void *cls, + const char *section, + const char *option, + const char *value) +{ + struct RequestHandle *handle = cls; + struct GNUNET_CRYPTO_EcdsaPublicKey pkey; + + if (GNUNET_OK != + GNUNET_CRYPTO_ecdsa_public_key_from_string (value, + strlen (value), + &pkey)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping non key %s\n", + value); + return; + } + if (0 == memcmp (&pkey, &handle->oidc->client_pkey, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + handle->tld = GNUNET_strdup (option+1); +} + +/** + * Responds to authorization GET and url-encoded POST request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_HashCode cache_key; + struct EgoEntry *tmp_ego; + const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; + struct GNUNET_CRYPTO_EcdsaPublicKey pkey; + + cookie_identity_interpretation (handle); + + //RECOMMENDED value: state - REQUIRED for answers + GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->oidc->state = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + handle->oidc->state = GNUNET_strdup (handle->oidc->state); + } + + // REQUIRED value: client_id + GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY), + &cache_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter client_id"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key)); + + if ( GNUNET_OK + != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id, + strlen (handle->oidc->client_id), + &handle->oidc->client_pkey) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT); + handle->edesc = GNUNET_strdup ("The client is not authorized to request an " + "authorization code using this method."); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + + if ( NULL == handle->ego_head ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("Egos are missing"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + handle->ego_entry = handle->ego_head; + handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego); + //If we know this identity, translated the corresponding TLD + //TODO: We might want to have a reverse lookup functionality for TLDs? + for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next) + { + priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego); + GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, + &pkey); + if (0 == memcmp (&pkey, &handle->oidc->client_pkey, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + { + handle->tld = GNUNET_strdup (tmp_ego->identifier); + handle->ego_entry = handle->ego_tail; + } + } + if (NULL == handle->tld) + GNUNET_CONFIGURATION_iterate_section_values (cfg, + "gns", + tld_iter, + handle); + if (NULL == handle->tld) + handle->tld = GNUNET_strdup (handle->oidc->client_id); + GNUNET_SCHEDULER_add_now (&build_authz_response, handle); +} + +/** + * Combines an identity with a login time and responds OK to login request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +login_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp = GNUNET_REST_create_response (""); + struct RequestHandle *handle = cls; + struct GNUNET_HashCode cache_key; + struct GNUNET_TIME_Absolute *current_time; + struct GNUNET_TIME_Absolute *last_time; + char* cookie; + char* header_val; + json_t *root; + json_error_t error; + json_t *identity; + char term_data[handle->rest_handle->data_size+1]; + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size); + root = json_loads (term_data, JSON_DECODE_ANY, &error); + identity = json_object_get (root, "identity"); + if (!json_is_string (identity)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error parsing json string from %s\n", + term_data); + handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST); + json_decref (root); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + return; + } + GNUNET_asprintf (&cookie, + "Identity=%s", + json_string_value (identity)); + GNUNET_asprintf (&header_val, + "%s;Max-Age=%d", + cookie, + OIDC_COOKIE_EXPIRATION); + MHD_add_response_header (resp, "Set-Cookie", header_val); + MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST"); + GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key); + + if (0 != strcmp (json_string_value (identity), + "Denied")) + { + current_time = GNUNET_new (struct GNUNET_TIME_Absolute); + *current_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (), + OIDC_COOKIE_EXPIRATION)); + last_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key); + GNUNET_free_non_null (last_time); + GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map, + &cache_key, + current_time, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (cookie); + GNUNET_free (header_val); + json_decref (root); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + +static int +check_authorization (struct RequestHandle *handle, + struct GNUNET_CRYPTO_EcdsaPublicKey *cid) +{ + struct GNUNET_HashCode cache_key; + char *authorization; + char *credentials; + char *basic_authorization; + char *client_id; + char *pass; + char *expected_pass; + int client_exists = GNUNET_NO; + + GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY, + strlen (OIDC_AUTHORIZATION_HEADER_KEY), + &cache_key); + if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, + &cache_key) ) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->edesc=GNUNET_strdup ("missing authorization"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map, + &cache_key); + + //split header in "Basic" and [content] + credentials = strtok (authorization, " "); + if (0 != strcmp ("Basic", credentials)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + credentials = strtok (NULL, " "); + if (NULL == credentials) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + GNUNET_STRINGS_base64_decode (credentials, + strlen (credentials), + (void**)&basic_authorization); + + if ( NULL == basic_authorization ) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + client_id = strtok (basic_authorization, ":"); + if ( NULL == client_id ) + { + GNUNET_free_non_null (basic_authorization); + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + pass = strtok (NULL, ":"); + if (NULL == pass) + { + GNUNET_free_non_null (basic_authorization); + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + + //check client password + if ( GNUNET_OK + == GNUNET_CONFIGURATION_get_value_string (cfg, + "reclaim-rest-plugin", + "psw", + &expected_pass) ) + { + if (0 != strcmp (expected_pass, pass)) + { + GNUNET_free_non_null (basic_authorization); + GNUNET_free (expected_pass); + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + GNUNET_free (expected_pass); + } + else + { + GNUNET_free_non_null (basic_authorization); + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("gnunet configuration failed"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + return GNUNET_SYSERR; + } + + //check client_id + for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry; ) + { + if (0 == strcmp (handle->ego_entry->keystring, client_id)) + { + client_exists = GNUNET_YES; + break; + } + handle->ego_entry = handle->ego_entry->next; + } + if (GNUNET_NO == client_exists) + { + GNUNET_free_non_null (basic_authorization); + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + return GNUNET_SYSERR; + } + GNUNET_STRINGS_string_to_data (client_id, + strlen (client_id), + cid, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + + GNUNET_free (basic_authorization); + return GNUNET_OK; +} + +static int +ego_exists (struct RequestHandle *handle, + struct GNUNET_CRYPTO_EcdsaPublicKey *test_key) +{ + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; + + for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) + { + GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key); + if (0 == memcmp (&pub_key, + test_key, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + { + break; + } + } + if (NULL == ego_entry) + return GNUNET_NO; + return GNUNET_YES; +} + +static void +store_ticket_reference (const struct RequestHandle *handle, + const char* access_token, + const struct GNUNET_RECLAIM_Ticket *ticket, + const struct GNUNET_CRYPTO_EcdsaPublicKey *cid) +{ + struct GNUNET_HashCode cache_key; + char *id_ticket_combination; + char *ticket_string; + char *client_id; + + GNUNET_CRYPTO_hash (access_token, + strlen (access_token), + &cache_key); + client_id = GNUNET_STRINGS_data_to_string_alloc (cid, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket, + sizeof (struct GNUNET_RECLAIM_Ticket)); + GNUNET_asprintf (&id_ticket_combination, + "%s;%s", + client_id, + ticket_string); + GNUNET_CONTAINER_multihashmap_put (OIDC_access_token_map, + &cache_key, + id_ticket_combination, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + + GNUNET_free (client_id); + GNUNET_free (ticket_string); +} + +/** + * Responds to token url-encoded POST request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_TIME_Relative expiration_time; + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl; + struct GNUNET_RECLAIM_Ticket *ticket; + struct GNUNET_CRYPTO_EcdsaPublicKey cid; + struct GNUNET_HashCode cache_key; + struct MHD_Response *resp; + char *grant_type; + char *code; + char *json_response; + char *id_token; + char *access_token; + char *jwt_secret; + char *nonce; + int i = 1; + + /* + * Check Authorization + */ + if (GNUNET_SYSERR == check_authorization (handle, + &cid)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "OIDC authorization for token endpoint failed\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + /* + * Check parameter + */ + + //TODO Do not allow multiple equal parameter names + //REQUIRED grant_type + GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key); + if (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter grant_type"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + + //REQUIRED code + GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key); + if (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter code"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &cache_key); + + //REQUIRED redirect_uri + GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY), + &cache_key); + if (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &cache_key) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter redirect_uri"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + //Check parameter grant_type == "authorization_code" + if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type)) + { + handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + GNUNET_CRYPTO_hash (code, strlen (code), &cache_key); + if (GNUNET_SYSERR == + GNUNET_CONTAINER_multihashmap_put (OIDC_used_ticket_map, + &cache_key, + &i, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("Cannot use the same code more than once"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + //decode code + if (GNUNET_OK != OIDC_parse_authz_code (&cid, + code, + &ticket, + &nonce)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("invalid code"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + //create jwt + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + "reclaim-rest-plugin", + "expiration_time", + &expiration_time)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); + handle->edesc = GNUNET_strdup ("gnunet configuration failed"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + GNUNET_free (ticket); + return; + } + + + //TODO OPTIONAL acr,amr,azp + if (GNUNET_NO == ego_exists (handle, + &ticket->audience)) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("invalid code..."); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + GNUNET_free (ticket); + } + if ( GNUNET_OK + != GNUNET_CONFIGURATION_get_value_string (cfg, + "reclaim-rest-plugin", + "jwt_secret", + &jwt_secret) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("No signing secret configured!"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + GNUNET_free (ticket); + return; + } + //TODO We should collect the attributes here. cl always empty + cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); + id_token = OIDC_id_token_new (&ticket->audience, + &ticket->identity, + cl, + &expiration_time, + (NULL != nonce) ? nonce : NULL, + jwt_secret); + access_token = OIDC_access_token_new (); + OIDC_build_token_response (access_token, + id_token, + &expiration_time, + &json_response); + + store_ticket_reference (handle, + access_token, + ticket, + &cid); + resp = GNUNET_REST_create_response (json_response); + MHD_add_response_header (resp, "Cache-Control", "no-store"); + MHD_add_response_header (resp, "Pragma", "no-cache"); + MHD_add_response_header (resp, "Content-Type", "application/json"); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl); + GNUNET_free (access_token); + GNUNET_free (json_response); + GNUNET_free (ticket); + GNUNET_free (id_token); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + +/** + * Collects claims and stores them in handle + */ +static void +consume_ticket (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) +{ + struct RequestHandle *handle = cls; + char *tmp_value; + json_t *value; + + if (NULL == identity) + { + GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle); + return; + } + tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, + attr->data, + attr->data_size); + value = json_string (tmp_value); + json_object_set_new (handle->oidc->response, + attr->name, + value); + GNUNET_free (tmp_value); +} + +/** + * Responds to userinfo GET and url-encoded POST request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, void *cls) +{ + //TODO expiration time + struct RequestHandle *handle = cls; + char delimiter[] = " "; + char delimiter_db[] = ";"; + struct GNUNET_HashCode cache_key; + char *authorization, *authorization_type, *authorization_access_token; + char *client_ticket, *client, *ticket_str; + struct GNUNET_RECLAIM_Ticket *ticket; + + GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY, + strlen (OIDC_AUTHORIZATION_HEADER_KEY), + &cache_key); + if ( GNUNET_NO + == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, + &cache_key) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("No Access Token"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + return; + } + authorization = GNUNET_CONTAINER_multihashmap_get ( + handle->rest_handle->header_param_map, &cache_key); + + //split header in "Bearer" and access_token + authorization = GNUNET_strdup (authorization); + authorization_type = strtok (authorization, delimiter); + if ( 0 != strcmp ("Bearer", authorization_type) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("No Access Token"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + return; + } + authorization_access_token = strtok (NULL, delimiter); + if ( NULL == authorization_access_token ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("No Access Token"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + return; + } + + GNUNET_CRYPTO_hash (authorization_access_token, + strlen (authorization_access_token), + &cache_key); + if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map, + &cache_key) ) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("The Access Token expired"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + return; + } + + client_ticket = GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, + &cache_key); + client_ticket = GNUNET_strdup (client_ticket); + client = strtok (client_ticket,delimiter_db); + if (NULL == client) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("The Access Token expired"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + GNUNET_free (client_ticket); + return; + } + handle->ego_entry = handle->ego_head; + for (; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next) + { + if (0 == strcmp (handle->ego_entry->keystring,client)) + break; + } + if (NULL == handle->ego_entry) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("The Access Token expired"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + GNUNET_free (client_ticket); + return; + } + ticket_str = strtok (NULL, delimiter_db); + if (NULL == ticket_str) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("The Access Token expired"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (authorization); + GNUNET_free (client_ticket); + return; + } + ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); + if ( GNUNET_OK + != GNUNET_STRINGS_string_to_data (ticket_str, + strlen (ticket_str), + ticket, + sizeof (struct GNUNET_RECLAIM_Ticket))) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); + handle->edesc = GNUNET_strdup ("The Access Token expired"); + handle->response_code = MHD_HTTP_UNAUTHORIZED; + GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); + GNUNET_free (ticket); + GNUNET_free (authorization); + GNUNET_free (client_ticket); + return; + } + + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->oidc->response = json_object (); + json_object_set_new (handle->oidc->response, + "sub", + json_string (handle->ego_entry->keystring)); + handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp, + GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego), + ticket, + consume_ticket, + handle); + GNUNET_free (ticket); + GNUNET_free (authorization); + GNUNET_free (client_ticket); + +} + + +/** + * Handle rest request + * + * @param handle the request handle + */ +static void +init_cont (struct RequestHandle *handle) +{ + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint }, + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, + &options_cont}, + GNUNET_REST_HANDLER_END + }; + + if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } +} + +/** + * If listing is enabled, prints information about the egos. + * + * This function is initially called for all egos and then again + * whenever a ego's identifier changes or if it is deleted. At the + * end of the initial pass over all egos, the function is once called + * with 'NULL' for 'ego'. That does NOT mean that the callback won't + * be invoked in the future or that there was an error. + * + * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', + * this function is only called ONCE, and 'NULL' being passed in + * 'ego' does indicate an error (i.e. name is taken or no default + * value is known). If 'ego' is non-NULL and if '*ctx' + * is set in those callbacks, the value WILL be passed to a subsequent + * call to the identity callback of 'GNUNET_IDENTITY_connect' (if + * that one was not NULL). + * + * When an identity is renamed, this function is called with the + * (known) ego but the NEW identifier. + * + * When an identity is deleted, this function is called with the + * (known) ego and "NULL" for the 'identifier'. In this case, + * the 'ego' is henceforth invalid (and the 'ctx' should also be + * cleaned up). + * + * @param cls closure + * @param ego ego handle + * @param ctx context for application to store data for this ego + * (during the lifetime of this process, initially NULL) + * @param identifier identifier assigned by the user for this ego, + * NULL if the user just deleted the ego and it + * must thus no longer be used + */ +static void +list_ego (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *identifier) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_EcdsaPublicKey pk; + + if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) + { + handle->state = ID_REST_STATE_POST_INIT; + init_cont (handle); + return; + } + if (ID_REST_STATE_INIT == handle->state) { + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = + GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, + handle->ego_tail, + ego_entry); + return; + } + /* Ego renamed or added */ + if (identifier != NULL) { + for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) { + if (ego_entry->ego == ego) { + /* Rename */ + GNUNET_free (ego_entry->identifier); + ego_entry->identifier = GNUNET_strdup (identifier); + break; + } + } + if (NULL == ego_entry) { + /* Add */ + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = + GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, + handle->ego_tail, + ego_entry); + } + } else { + /* Delete */ + for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) { + if (ego_entry->ego == ego) + break; + } + if (NULL != ego_entry) + GNUNET_CONTAINER_DLL_remove (handle->ego_head,handle->ego_tail, ego_entry); + } + +} + +static void +rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + handle->oidc = GNUNET_new (struct OIDC_Variables); + if (NULL == OIDC_cookie_jar_map) + OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); + if (NULL == OIDC_identity_grants) + OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); + if (NULL == OIDC_used_ticket_map) + OIDC_used_ticket_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); + if (NULL == OIDC_access_token_map) + OIDC_access_token_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); + handle->response_code = 0; + handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->state = ID_REST_STATE_INIT; + handle->rest_handle = rest_handle; + + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting...\n"); + handle->identity_handle = GNUNET_IDENTITY_connect (cfg, + &list_ego, + handle); + handle->gns_handle = GNUNET_GNS_connect (cfg); + handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg); + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_timeout, + handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected\n"); +} + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_openid_connect_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_OIDC; + api->process_request = &rest_identity_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Identity Provider REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_openid_connect_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + plugin->cfg = NULL; + + struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it; + void *value = NULL; + hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map); + while (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) + GNUNET_free_non_null (value); + GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map); + + hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants); + while (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) + GNUNET_free_non_null (value); + GNUNET_CONTAINER_multihashmap_destroy (OIDC_identity_grants); + + hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_used_ticket_map); + while (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) + GNUNET_free_non_null (value); + GNUNET_CONTAINER_multihashmap_destroy (OIDC_used_ticket_map); + + hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map); + while (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) + GNUNET_free_non_null (value); + GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map); + GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it); + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Identity Provider REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_identity_provider.c */ diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c new file mode 100644 index 000000000..b36ed2bb6 --- /dev/null +++ b/src/reclaim/plugin_rest_reclaim.c @@ -0,0 +1,1104 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Martin Schanzenbach + * @author Philippe Buschmann + * @file reclaim/plugin_rest_reclaim.c + * @brief GNUnet reclaim REST plugin + * + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_identity_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_namestore_service.h" +#include "gnunet_rest_lib.h" +#include "microhttpd.h" +#include +#include +#include "gnunet_signatures.h" +#include "gnunet_reclaim_attribute_lib.h" +#include "gnunet_reclaim_service.h" +#include "json_reclaim.h" + +/** + * REST root namespace + */ +#define GNUNET_REST_API_NS_RECLAIM "/reclaim" + +/** + * Attribute namespace + */ +#define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes" + +/** + * Ticket namespace + */ +#define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets" + +/** + * Revoke namespace + */ +#define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke" + +/** + * Revoke namespace + */ +#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume" + +/** + * State while collecting all egos + */ +#define ID_REST_STATE_INIT 0 + +/** + * Done collecting egos + */ +#define ID_REST_STATE_POST_INIT 1 + +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * The ego list + */ +struct EgoEntry +{ + /** + * DLL + */ + struct EgoEntry *next; + + /** + * DLL + */ + struct EgoEntry *prev; + + /** + * Ego Identifier + */ + char *identifier; + + /** + * Public key string + */ + char *keystring; + + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; +}; + + +struct RequestHandle +{ + /** + * Ego list + */ + struct EgoEntry *ego_head; + + /** + * Ego list + */ + struct EgoEntry *ego_tail; + + /** + * Selected ego + */ + struct EgoEntry *ego_entry; + + /** + * Pointer to ego private key + */ + struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key; + + /** + * The processing state + */ + int state; + + /** + * Handle to Identity service. + */ + struct GNUNET_IDENTITY_Handle *identity_handle; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * Handle to NAMESTORE + */ + struct GNUNET_NAMESTORE_Handle *namestore_handle; + + /** + * Iterator for NAMESTORE + */ + struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it; + + /** + * Attribute claim list + */ + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list; + + /** + * IDENTITY Operation + */ + struct GNUNET_IDENTITY_Operation *op; + + /** + * Identity Provider + */ + struct GNUNET_RECLAIM_Handle *idp; + + /** + * Idp Operation + */ + struct GNUNET_RECLAIM_Operation *idp_op; + + /** + * Attribute iterator + */ + struct GNUNET_RECLAIM_AttributeIterator *attr_it; + + /** + * Ticket iterator + */ + struct GNUNET_RECLAIM_TicketIterator *ticket_it; + + /** + * A ticket + */ + struct GNUNET_RECLAIM_Ticket ticket; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Error response message + */ + char *emsg; + + /** + * Reponse code + */ + int response_code; + + /** + * Response object + */ + json_t *resp_object; + +}; + +/** + * Cleanup lookup handle + * @param handle Handle to clean up + */ +static void +cleanup_handle (struct RequestHandle *handle) +{ + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry; + struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp; + struct EgoEntry *ego_entry; + struct EgoEntry *ego_tmp; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + if (NULL != handle->resp_object) + json_decref (handle->resp_object); + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel (handle->timeout_task); + if (NULL != handle->identity_handle) + GNUNET_IDENTITY_disconnect (handle->identity_handle); + if (NULL != handle->attr_it) + GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); + if (NULL != handle->ticket_it) + GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); + if (NULL != handle->idp) + GNUNET_RECLAIM_disconnect (handle->idp); + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->emsg) + GNUNET_free (handle->emsg); + if (NULL != handle->namestore_handle) + GNUNET_NAMESTORE_disconnect (handle->namestore_handle); + if ( NULL != handle->attr_list ) + { + for (claim_entry = handle->attr_list->list_head; + NULL != claim_entry;) + { + claim_tmp = claim_entry; + claim_entry = claim_entry->next; + GNUNET_free(claim_tmp->claim); + GNUNET_free(claim_tmp); + } + GNUNET_free (handle->attr_list); + } + for (ego_entry = handle->ego_head; + NULL != ego_entry;) + { + ego_tmp = ego_entry; + ego_entry = ego_entry->next; + GNUNET_free (ego_tmp->identifier); + GNUNET_free (ego_tmp->keystring); + GNUNET_free (ego_tmp); + } + if (NULL != handle->attr_it) + { + GNUNET_free(handle->attr_it); + } + GNUNET_free (handle); +} + +static void +cleanup_handle_delayed (void *cls) +{ + cleanup_handle (cls); +} + + +/** + * Task run on error, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *json_error; + + GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", + handle->emsg); + if ( 0 == handle->response_code ) + { + handle->response_code = MHD_HTTP_BAD_REQUEST; + } + resp = GNUNET_REST_create_response (json_error); + MHD_add_response_header (resp, "Content-Type", "application/json"); + handle->proc (handle->proc_cls, resp, handle->response_code); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + GNUNET_free (json_error); +} + + +/** + * Task run on timeout, sends error message. Cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_timeout (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->timeout_task = NULL; + do_error (handle); +} + + +static void +collect_error_cb (void *cls) +{ + struct RequestHandle *handle = cls; + + do_error (handle); +} + +static void +finished_cont (void *cls, + int32_t success, + const char *emsg) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + resp = GNUNET_REST_create_response (emsg); + if (GNUNET_OK != success) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + + +/** + * Return attributes for identity + * + * @param cls the request handle + */ +static void +return_response (void *cls) +{ + char* result_str; + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + result_str = json_dumps (handle->resp_object, 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); + resp = GNUNET_REST_create_response (result_str); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (result_str); + cleanup_handle (handle); +} + +static void +collect_finished_cb (void *cls) +{ + struct RequestHandle *handle = cls; + //Done + handle->attr_it = NULL; + handle->ticket_it = NULL; + GNUNET_SCHEDULER_add_now (&return_response, handle); +} + + +/** + * Collect all attributes for an ego + * + */ +static void +ticket_collect (void *cls, + const struct GNUNET_RECLAIM_Ticket *ticket) +{ + json_t *json_resource; + struct RequestHandle *handle = cls; + json_t *value; + char* tmp; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n"); + tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, + sizeof (uint64_t)); + json_resource = json_object (); + GNUNET_free (tmp); + json_array_append (handle->resp_object, + json_resource); + + tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + value = json_string (tmp); + json_object_set_new (json_resource, + "issuer", + value); + GNUNET_free (tmp); + tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + value = json_string (tmp); + json_object_set_new (json_resource, + "audience", + value); + GNUNET_free (tmp); + tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, + sizeof (uint64_t)); + value = json_string (tmp); + json_object_set_new (json_resource, + "rnd", + value); + GNUNET_free (tmp); + GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it); +} + + + +/** + * List tickets for identity request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *identity; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n", + handle->url); + if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >= + strlen (handle->url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1; + + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + if (0 == strcmp (identity, ego_entry->identifier)) + break; + handle->resp_object = json_array (); + + if (NULL == ego_entry) + { + //Done + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", + identity); + GNUNET_SCHEDULER_add_now (&return_response, handle); + return; + } + priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->ticket_it = GNUNET_RECLAIM_ticket_iteration_start (handle->idp, + priv_key, + &collect_error_cb, + handle, + &ticket_collect, + handle, + &collect_finished_cb, + handle); +} + + +static void +add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; + const char* identity; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attribute; + struct GNUNET_TIME_Relative exp; + char term_data[handle->rest_handle->data_size+1]; + json_t *data_json; + json_error_t err; + struct GNUNET_JSON_Specification attrspec[] = { + GNUNET_RECLAIM_JSON_spec_claim (&attribute), + GNUNET_JSON_spec_end() + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n", + handle->url); + if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= + strlen (handle->url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1; + + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + if (0 == strcmp (identity, ego_entry->identifier)) + break; + + if (NULL == ego_entry) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Identity unknown (%s)\n", identity); + return; + } + identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + + if (0 >= handle->rest_handle->data_size) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_json = json_loads (term_data, + JSON_DECODE_ANY, + &err); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (data_json, attrspec, + NULL, NULL)); + json_decref (data_json); + if (NULL == attribute) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse attribute from %s\n", + term_data); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->idp = GNUNET_RECLAIM_connect (cfg); + exp = GNUNET_TIME_UNIT_HOURS; + handle->idp_op = GNUNET_RECLAIM_attribute_store (handle->idp, + identity_priv, + attribute, + &exp, + &finished_cont, + handle); + GNUNET_JSON_parse_free (attrspec); +} + + + +/** + * Collect all attributes for an ego + * + */ +static void +attr_collect (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) +{ + struct RequestHandle *handle = cls; + json_t *attr_obj; + const char* type; + char* tmp_value; + + if ((NULL == attr->name) || (NULL == attr->data)) + { + GNUNET_RECLAIM_get_attributes_next (handle->attr_it); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", + attr->name); + + tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, + attr->data, + attr->data_size); + + attr_obj = json_object (); + json_object_set_new (attr_obj, + "value", + json_string (tmp_value)); + json_object_set_new (attr_obj, + "name", + json_string (attr->name)); + type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type); + json_object_set_new (attr_obj, + "type", + json_string (type)); + json_array_append (handle->resp_object, + attr_obj); + json_decref (attr_obj); + GNUNET_free(tmp_value); + GNUNET_RECLAIM_get_attributes_next (handle->attr_it); +} + + + +/** + * List attributes for identity request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *identity; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n", + handle->url); + if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= + strlen (handle->url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1; + + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + if (0 == strcmp (identity, ego_entry->identifier)) + break; + handle->resp_object = json_array (); + + + if (NULL == ego_entry) + { + //Done + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", + identity); + GNUNET_SCHEDULER_add_now (&return_response, handle); + return; + } + priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp, + priv_key, + &collect_error_cb, + handle, + &attr_collect, + handle, + &collect_finished_cb, + handle); +} + + +static void +revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_RECLAIM_Ticket *ticket; + struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk; + char term_data[handle->rest_handle->data_size+1]; + json_t *data_json; + json_error_t err; + struct GNUNET_JSON_Specification tktspec[] = { + GNUNET_RECLAIM_JSON_spec_ticket (&ticket), + GNUNET_JSON_spec_end() + }; + + if (0 >= handle->rest_handle->data_size) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_json = json_loads (term_data, + JSON_DECODE_ANY, + &err); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (data_json, tktspec, + NULL, NULL)); + json_decref (data_json); + if (NULL == ticket) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse ticket from %s\n", + term_data); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (GNUNET_OK != GNUNET_JSON_parse (data_json, + tktspec, + NULL, NULL)) + { + handle->emsg = GNUNET_strdup ("Not a ticket!\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + GNUNET_JSON_parse_free (tktspec); + json_decref (data_json); + return; + } + + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + { + GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, + &tmp_pk); + if (0 == memcmp (&ticket->identity, + &tmp_pk, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + break; + } + if (NULL == ego_entry) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Identity unknown\n"); + GNUNET_JSON_parse_free (tktspec); + return; + } + identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->idp_op = GNUNET_RECLAIM_ticket_revoke (handle->idp, + identity_priv, + ticket, + &finished_cont, + handle); + GNUNET_JSON_parse_free (tktspec); +} + +static void +consume_cont (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) +{ + struct RequestHandle *handle = cls; + char *val_str; + json_t *value; + + if (NULL == identity) + { + GNUNET_SCHEDULER_add_now (&return_response, handle); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", + attr->name); + val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, + attr->data, + attr->data_size); + if (NULL == val_str) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse value for: %s\n", + attr->name); + return; + } + value = json_string(val_str); + json_object_set_new (handle->resp_object, + attr->name, + value); + json_decref (value); + GNUNET_free (val_str); +} + +static void +consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_RECLAIM_Ticket *ticket; + struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk; + char term_data[handle->rest_handle->data_size+1]; + json_t *data_json; + json_error_t err; + struct GNUNET_JSON_Specification tktspec[] = { + GNUNET_RECLAIM_JSON_spec_ticket (&ticket), + GNUNET_JSON_spec_end () + }; + + if (0 >= handle->rest_handle->data_size) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_json = json_loads (term_data, + JSON_DECODE_ANY, + &err); + if (NULL == data_json) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse JSON Object from %s\n", + term_data); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (GNUNET_OK != GNUNET_JSON_parse (data_json, + tktspec, + NULL, NULL)) + { + handle->emsg = GNUNET_strdup ("Not a ticket!\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + GNUNET_JSON_parse_free(tktspec); + json_decref (data_json); + return; + } + for (ego_entry = handle->ego_head; + NULL != ego_entry; + ego_entry = ego_entry->next) + { + GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, + &tmp_pk); + if (0 == memcmp (&ticket->audience, + &tmp_pk, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + break; + } + if (NULL == ego_entry) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Identity unknown\n"); + GNUNET_JSON_parse_free (tktspec); + return; + } + identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + handle->resp_object = json_object (); + handle->idp = GNUNET_RECLAIM_connect (cfg); + handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp, + identity_priv, + ticket, + &consume_cont, + handle); + GNUNET_JSON_parse_free (tktspec); +} + + + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //For now, independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + cleanup_handle (handle); + return; +} + +/** + * Handle rest request + * + * @param handle the request handle + */ +static void +init_cont (struct RequestHandle *handle) +{ + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont}, + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont}, + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM, + &options_cont}, + GNUNET_REST_HANDLER_END + }; + + if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } +} + +/** + * If listing is enabled, prints information about the egos. + * + * This function is initially called for all egos and then again + * whenever a ego's identifier changes or if it is deleted. At the + * end of the initial pass over all egos, the function is once called + * with 'NULL' for 'ego'. That does NOT mean that the callback won't + * be invoked in the future or that there was an error. + * + * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', + * this function is only called ONCE, and 'NULL' being passed in + * 'ego' does indicate an error (i.e. name is taken or no default + * value is known). If 'ego' is non-NULL and if '*ctx' + * is set in those callbacks, the value WILL be passed to a subsequent + * call to the identity callback of 'GNUNET_IDENTITY_connect' (if + * that one was not NULL). + * + * When an identity is renamed, this function is called with the + * (known) ego but the NEW identifier. + * + * When an identity is deleted, this function is called with the + * (known) ego and "NULL" for the 'identifier'. In this case, + * the 'ego' is henceforth invalid (and the 'ctx' should also be + * cleaned up). + * + * @param cls closure + * @param ego ego handle + * @param ctx context for application to store data for this ego + * (during the lifetime of this process, initially NULL) + * @param identifier identifier assigned by the user for this ego, + * NULL if the user just deleted the ego and it + * must thus no longer be used + */ +static void +list_ego (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *identifier) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_EcdsaPublicKey pk; + + if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) + { + handle->state = ID_REST_STATE_POST_INIT; + init_cont (handle); + return; + } + if (ID_REST_STATE_INIT == handle->state) { + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = + GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry); + } + +} + +static void +rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + handle->response_code = 0; + handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->state = ID_REST_STATE_INIT; + handle->rest_handle = rest_handle; + + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting...\n"); + handle->identity_handle = GNUNET_IDENTITY_connect (cfg, + &list_ego, + handle); + handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg); + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_timeout, + handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected\n"); +} + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_reclaim_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_RECLAIM; + api->process_request = &rest_identity_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Identity Provider REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_reclaim_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + plugin->cfg = NULL; + + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Identity Provider REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_reclaim.c */ diff --git a/src/rest-plugins/Makefile.am b/src/rest-plugins/Makefile.am index 006c23101..cec9348a6 100644 --- a/src/rest-plugins/Makefile.am +++ b/src/rest-plugins/Makefile.am @@ -21,28 +21,7 @@ plugin_LTLIBRARIES = \ libgnunet_plugin_rest_copying.la \ libgnunet_plugin_rest_peerinfo.la \ libgnunet_plugin_rest_identity.la \ - libgnunet_plugin_rest_namestore.la \ - libgnunet_plugin_rest_gns.la -if HAVE_ABE -plugin_LTLIBRARIES += libgnunet_plugin_rest_openid_connect.la \ - libgnunet_plugin_rest_reclaim.la -endif - -libgnunet_plugin_rest_reclaim_la_SOURCES = \ - plugin_rest_reclaim.c \ - json_reclaim.h \ - json_reclaim.c -libgnunet_plugin_rest_reclaim_la_LIBADD = \ - $(top_builddir)/src/identity/libgnunetidentity.la \ - $(top_builddir)/src/reclaim/libgnunetreclaim.la \ - $(top_builddir)/src/json/libgnunetjson.la \ - $(top_builddir)/src/rest/libgnunetrest.la \ - $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson -lmicrohttpd -libgnunet_plugin_rest_reclaim_la_LDFLAGS = \ - i$(GN_PLUGIN_LDFLAGS) + libgnunet_plugin_rest_namestore.la #libgnunet_plugin_rest_credential_la_SOURCES = \ @@ -103,33 +82,4 @@ libgnunet_plugin_rest_namestore_la_LIBADD = \ libgnunet_plugin_rest_namestore_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) -libgnunet_plugin_rest_gns_la_SOURCES = \ - plugin_rest_gns.c -libgnunet_plugin_rest_gns_la_LIBADD = \ - $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/gns/libgnunetgns.la \ - $(top_builddir)/src/rest/libgnunetrest.la \ - $(top_builddir)/src/identity/libgnunetidentity.la \ - $(top_builddir)/src/json/libgnunetjson.la \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson -lmicrohttpd -libgnunet_plugin_rest_gns_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_plugin_rest_openid_connect_la_SOURCES = \ - plugin_rest_openid_connect.c \ - oidc_helper.h \ - oidc_helper.c -libgnunet_plugin_rest_openid_connect_la_LIBADD = \ - $(top_builddir)/src/identity/libgnunetidentity.la \ - $(top_builddir)/src/reclaim/libgnunetreclaim.la \ - $(top_builddir)/src/rest/libgnunetrest.la \ - $(top_builddir)/src/reclaim-attribute/libgnunetreclaimattribute.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ -$(top_builddir)/src/gns/libgnunetgns.la \ - $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson -lmicrohttpd -libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) diff --git a/src/rest-plugins/json_reclaim.c b/src/rest-plugins/json_reclaim.c deleted file mode 100644 index 0fe9150d9..000000000 --- a/src/rest-plugins/json_reclaim.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later -*/ - -/** - * @file rest-plugins/json_reclaim.c - * @brief JSON handling of reclaim data - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_json_lib.h" -#include "gnunet_reclaim_service.h" -#include "gnunet_reclaim_attribute_lib.h" - -/** - * Parse given JSON object to a claim - * - * @param cls closure, NULL - * @param root the json object representing data - * @param spec where to write the data - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -static int -parse_attr (void *cls, - json_t *root, - struct GNUNET_JSON_Specification *spec) -{ - struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr; - const char* name_str; - const char* val_str; - const char* type_str; - char *data; - int unpack_state; - uint32_t type; - size_t data_size; - - GNUNET_assert(NULL != root); - - if(!json_is_object(root)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error json is not array nor object!\n"); - return GNUNET_SYSERR; - } - //interpret single attribute - unpack_state = json_unpack(root, - "{s:s, s:s, s:s!}", - "name", &name_str, - "type", &type_str, - "value", &val_str); - if (0 != unpack_state) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Error json object has a wrong format!\n"); - return GNUNET_SYSERR; - } - type = GNUNET_RECLAIM_ATTRIBUTE_typename_to_number (type_str); - if (GNUNET_SYSERR == (GNUNET_RECLAIM_ATTRIBUTE_string_to_value (type, - val_str, - (void**)&data, - &data_size))) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Attribute value invalid!\n"); - return GNUNET_SYSERR; - } - attr = GNUNET_RECLAIM_ATTRIBUTE_claim_new (name_str, - type, - data, - data_size); - *(struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr = attr; - return GNUNET_OK; -} - -/** - * Cleanup data left from parsing RSA public key. - * - * @param cls closure, NULL - * @param[out] spec where to free the data - */ -static void -clean_attr (void *cls, struct GNUNET_JSON_Specification *spec) -{ - struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr; - attr = (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **) spec->ptr; - if (NULL != *attr) - { - GNUNET_free(*attr); - *attr = NULL; - } -} - -/** - * JSON Specification for Reclaim claims. - * - * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill - * @return JSON Specification - */ -struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr) -{ - struct GNUNET_JSON_Specification ret = { - .parser = &parse_attr, - .cleaner = &clean_attr, - .cls = NULL, - .field = NULL, - .ptr = attr, - .ptr_size = 0, - .size_ptr = NULL - }; - *attr = NULL; - return ret; -} -/** - * Parse given JSON object to a ticket - * - * @param cls closure, NULL - * @param root the json object representing data - * @param spec where to write the data - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -static int -parse_ticket (void *cls, - json_t *root, - struct GNUNET_JSON_Specification *spec) -{ - struct GNUNET_RECLAIM_Ticket *ticket; - const char* rnd_str; - const char* aud_str; - const char* id_str; - int unpack_state; - - GNUNET_assert(NULL != root); - - if(!json_is_object(root)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error json is not array nor object!\n"); - return GNUNET_SYSERR; - } - //interpret single ticket - unpack_state = json_unpack(root, - "{s:s, s:s, s:s!}", - "rnd", &rnd_str, - "audience", &aud_str, - "identity", &id_str); - if (0 != unpack_state) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, - "Error json object has a wrong format!\n"); - return GNUNET_SYSERR; - } - ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); - if (GNUNET_OK != GNUNET_STRINGS_string_to_data (rnd_str, - strlen (rnd_str), - &ticket->rnd, - sizeof (uint64_t))) - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Rnd invalid\n"); - GNUNET_free(ticket); - return GNUNET_SYSERR; - } - GNUNET_STRINGS_string_to_data (id_str, - strlen (id_str), - &ticket->identity, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Identity invalid\n"); - GNUNET_free(ticket); - return GNUNET_SYSERR; - } - - GNUNET_STRINGS_string_to_data (aud_str, - strlen (aud_str), - &ticket->audience, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - { - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,"Audience invalid\n"); - GNUNET_free(ticket); - return GNUNET_SYSERR; - } - - *(struct GNUNET_RECLAIM_Ticket **) spec->ptr = ticket; - return GNUNET_OK; -} - -/** - * Cleanup data left from parsing RSA public key. - * - * @param cls closure, NULL - * @param[out] spec where to free the data - */ -static void -clean_ticket (void *cls, struct GNUNET_JSON_Specification *spec) -{ - struct GNUNET_RECLAIM_Ticket **ticket; - ticket = (struct GNUNET_RECLAIM_Ticket **) spec->ptr; - if (NULL != *ticket) - { - GNUNET_free(*ticket); - *ticket = NULL; - } -} - -/** - * JSON Specification for Reclaim tickets. - * - * @param ticket struct of GNUNET_RECLAIM_Ticket to fill - * @return JSON Specification - */ -struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket) -{ - struct GNUNET_JSON_Specification ret = { - .parser = &parse_ticket, - .cleaner = &clean_ticket, - .cls = NULL, - .field = NULL, - .ptr = ticket, - .ptr_size = 0, - .size_ptr = NULL - }; - *ticket = NULL; - return ret; -} diff --git a/src/rest-plugins/json_reclaim.h b/src/rest-plugins/json_reclaim.h deleted file mode 100644 index ced2e10dd..000000000 --- a/src/rest-plugins/json_reclaim.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later -*/ - -/** - * @file rest-plugins/json_reclaim.h - * @brief JSON handling of reclaim data - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_json_lib.h" -#include "gnunet_reclaim_service.h" -#include "gnunet_reclaim_attribute_lib.h" - -/** - * JSON Specification for Reclaim claims. - * - * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill - * @return JSON Specification - */ -struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_ATTRIBUTE_Claim **attr); - -/** - * JSON Specification for Reclaim tickets. - * - * @param ticket struct of GNUNET_RECLAIM_Ticket to fill - * @return JSON Specification - */ -struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket); diff --git a/src/rest-plugins/oidc_helper.c b/src/rest-plugins/oidc_helper.c deleted file mode 100644 index 646e58551..000000000 --- a/src/rest-plugins/oidc_helper.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file reclaim/oidc_helper.c - * @brief helper library for OIDC related functions - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_signatures.h" -#include "gnunet_reclaim_service.h" -#include "gnunet_reclaim_attribute_lib.h" -#include -#include -#include "oidc_helper.h" - -static char* -create_jwt_header(void) -{ - json_t *root; - char *json_str; - - root = json_object (); - json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE)); - json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE)); - - json_str = json_dumps (root, JSON_INDENT(0) | JSON_COMPACT); - json_decref (root); - return json_str; -} - -static void -replace_char(char* str, char find, char replace){ - char *current_pos = strchr(str,find); - while (current_pos){ - *current_pos = replace; - current_pos = strchr(current_pos,find); - } -} - -//RFC4648 -static void -fix_base64(char* str) { - //Replace + with - - replace_char (str, '+', '-'); - - //Replace / with _ - replace_char (str, '/', '_'); - -} - -/** - * Create a JWT from attributes - * - * @param aud_key the public of the audience - * @param sub_key the public key of the subject - * @param attrs the attribute list - * @param expiration_time the validity of the token - * @param secret_key the key used to sign the JWT - * @return a new base64-encoded JWT string. - */ -char* -OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, - const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, - const struct GNUNET_TIME_Relative *expiration_time, - const char *nonce, - const char *secret_key) -{ - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; - struct GNUNET_HashCode signature; - struct GNUNET_TIME_Absolute exp_time; - struct GNUNET_TIME_Absolute time_now; - char* audience; - char* subject; - char* header; - char* body_str; - char* result; - char* header_base64; - char* body_base64; - char* signature_target; - char* signature_base64; - char* attr_val_str; - json_t* body; - - //iat REQUIRED time now - time_now = GNUNET_TIME_absolute_get(); - //exp REQUIRED time expired from config - exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time); - //auth_time only if max_age - //nonce only if nonce - // OPTIONAL acr,amr,azp - subject = GNUNET_STRINGS_data_to_string_alloc (sub_key, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - audience = GNUNET_STRINGS_data_to_string_alloc (aud_key, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - header = create_jwt_header (); - body = json_object (); - - //iss REQUIRED case sensitive server uri with https - //The issuer is the local reclaim instance (e.g. https://reclaim.id/api/openid) - json_object_set_new (body, - "iss", json_string (SERVER_ADDRESS)); - //sub REQUIRED public key identity, not exceed 255 ASCII length - json_object_set_new (body, - "sub", json_string (subject)); - //aud REQUIRED public key client_id must be there - json_object_set_new (body, - "aud", json_string (audience)); - //iat - json_object_set_new (body, - "iat", json_integer (time_now.abs_value_us / (1000*1000))); - //exp - json_object_set_new (body, - "exp", json_integer (exp_time.abs_value_us / (1000*1000))); - //nbf - json_object_set_new (body, - "nbf", json_integer (time_now.abs_value_us / (1000*1000))); - //nonce - if (NULL != nonce) - json_object_set_new (body, - "nonce", json_string (nonce)); - - for (le = attrs->list_head; NULL != le; le = le->next) - { - attr_val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type, - le->claim->data, - le->claim->data_size); - json_object_set_new (body, - le->claim->name, - json_string (attr_val_str)); - GNUNET_free (attr_val_str); - } - body_str = json_dumps (body, JSON_INDENT(0) | JSON_COMPACT); - json_decref (body); - - GNUNET_STRINGS_base64_encode (header, - strlen (header), - &header_base64); - fix_base64(header_base64); - - GNUNET_STRINGS_base64_encode (body_str, - strlen (body_str), - &body_base64); - fix_base64(body_base64); - - GNUNET_free (subject); - GNUNET_free (audience); - - /** - * Creating the JWT signature. This might not be - * standards compliant, check. - */ - GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64); - GNUNET_CRYPTO_hmac_raw (secret_key, strlen (secret_key), signature_target, strlen (signature_target), &signature); - GNUNET_STRINGS_base64_encode ((const char*)&signature, - sizeof (struct GNUNET_HashCode), - &signature_base64); - fix_base64(signature_base64); - - GNUNET_asprintf (&result, "%s.%s.%s", - header_base64, body_base64, signature_base64); - - GNUNET_free (signature_target); - GNUNET_free (header); - GNUNET_free (body_str); - GNUNET_free (signature_base64); - GNUNET_free (body_base64); - GNUNET_free (header_base64); - return result; -} -/** - * Builds an OIDC authorization code including - * a reclaim ticket and nonce - * - * @param issuer the issuer of the ticket, used to sign the ticket and nonce - * @param ticket the ticket to include in the code - * @param nonce the nonce to include in the code - * @return a new authorization code (caller must free) - */ -char* -OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, - const struct GNUNET_RECLAIM_Ticket *ticket, - const char* nonce) -{ - char *ticket_str; - json_t *code_json; - char *signature_payload; - char *signature_str; - char *authz_code; - size_t signature_payload_len; - struct GNUNET_CRYPTO_EcdsaSignature signature; - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; - - signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket); - if (NULL != nonce) - signature_payload_len += strlen (nonce); - - signature_payload = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); - purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *)signature_payload; - purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); - purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); - memcpy (&purpose[1], - ticket, - sizeof (struct GNUNET_RECLAIM_Ticket)); - if (NULL != nonce) - memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket), - nonce, - strlen (nonce)); - if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_sign (issuer, - purpose, - &signature)) - { - GNUNET_free (signature_payload); - return NULL; - } - signature_str = GNUNET_STRINGS_data_to_string_alloc (&signature, - sizeof (signature)); - ticket_str = GNUNET_STRINGS_data_to_string_alloc (ticket, - sizeof (struct GNUNET_RECLAIM_Ticket)); - - code_json = json_object (); - json_object_set_new (code_json, - "ticket", - json_string (ticket_str)); - if (NULL != nonce) - json_object_set_new (code_json, - "nonce", - json_string (nonce)); - json_object_set_new (code_json, - "signature", - json_string (signature_str)); - authz_code = json_dumps (code_json, - JSON_INDENT(0) | JSON_COMPACT); - GNUNET_free (signature_payload); - GNUNET_free (signature_str); - GNUNET_free (ticket_str); - json_decref (code_json); - return authz_code; -} - - - - -/** - * Parse reclaim ticket and nonce from - * authorization code. - * This also verifies the signature in the code. - * - * @param audience the expected audience of the code - * @param code the string representation of the code - * @param ticket where to store the ticket - * @param nonce where to store the nonce - * @return GNUNET_OK if successful, else GNUNET_SYSERR - */ -int -OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, - const char* code, - struct GNUNET_RECLAIM_Ticket **ticket, - char **nonce) -{ - json_error_t error; - json_t *code_json; - json_t *ticket_json; - json_t *nonce_json; - json_t *signature_json; - const char *ticket_str; - const char *signature_str; - const char *nonce_str; - char *code_output; - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; - struct GNUNET_CRYPTO_EcdsaSignature signature; - size_t signature_payload_len; - - code_output = NULL; - GNUNET_STRINGS_base64_decode (code, - strlen(code), - (void**)&code_output); - code_json = json_loads (code_output, 0 , &error); - GNUNET_free (code_output); - ticket_json = json_object_get (code_json, "ticket"); - nonce_json = json_object_get (code_json, "nonce"); - signature_json = json_object_get (code_json, "signature"); - *ticket = NULL; - *nonce = NULL; - - if ((NULL == ticket_json || !json_is_string (ticket_json)) || - (NULL == signature_json || !json_is_string (signature_json))) - { - json_decref (code_json); - return GNUNET_SYSERR; - } - ticket_str = json_string_value (ticket_json); - signature_str = json_string_value (signature_json); - nonce_str = NULL; - if (NULL != nonce_json) - nonce_str = json_string_value (nonce_json); - signature_payload_len = sizeof (struct GNUNET_RECLAIM_Ticket); - if (NULL != nonce_str) - signature_payload_len += strlen (nonce_str); - purpose = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + - signature_payload_len); - purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + signature_payload_len); - purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); - if (GNUNET_OK != GNUNET_STRINGS_string_to_data (ticket_str, - strlen (ticket_str), - &purpose[1], - sizeof (struct GNUNET_RECLAIM_Ticket))) - { - GNUNET_free (purpose); - json_decref (code_json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot parse ticket!\n"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != GNUNET_STRINGS_string_to_data (signature_str, - strlen (signature_str), - &signature, - sizeof (struct GNUNET_CRYPTO_EcdsaSignature))) - { - GNUNET_free (purpose); - json_decref (code_json); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot parse signature!\n"); - return GNUNET_SYSERR; - } - *ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); - memcpy (*ticket, - &purpose[1], - sizeof (struct GNUNET_RECLAIM_Ticket)); - if (0 != memcmp (audience, - &(*ticket)->audience, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - { - GNUNET_free (purpose); - GNUNET_free (*ticket); - json_decref (code_json); - *ticket = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Audience in ticket does not match client!\n"); - return GNUNET_SYSERR; - - } - if (NULL != nonce_str) - memcpy (((char*)&purpose[1]) + sizeof (struct GNUNET_RECLAIM_Ticket), - nonce_str, - strlen (nonce_str)); - if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN, - purpose, - &signature, - &(*ticket)->identity)) - { - GNUNET_free (purpose); - GNUNET_free (*ticket); - json_decref (code_json); - *ticket = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Signature of authZ code invalid!\n"); - return GNUNET_SYSERR; - } - *nonce = GNUNET_strdup (nonce_str); - return GNUNET_OK; -} - -/** - * Build a token response for a token request - * TODO: Maybe we should add the scope here? - * - * @param access_token the access token to include - * @param id_token the id_token to include - * @param expiration_time the expiration time of the token(s) - * @param token_response where to store the response - */ -void -OIDC_build_token_response (const char *access_token, - const char *id_token, - const struct GNUNET_TIME_Relative *expiration_time, - char **token_response) -{ - json_t *root_json; - - root_json = json_object (); - - GNUNET_assert (NULL != access_token); - GNUNET_assert (NULL != id_token); - GNUNET_assert (NULL != expiration_time); - json_object_set_new (root_json, - "access_token", - json_string (access_token)); - json_object_set_new (root_json, - "token_type", - json_string ("Bearer")); - json_object_set_new (root_json, - "expires_in", - json_integer (expiration_time->rel_value_us / (1000 * 1000))); - json_object_set_new (root_json, - "id_token", - json_string (id_token)); - *token_response = json_dumps (root_json, - JSON_INDENT(0) | JSON_COMPACT); - json_decref (root_json); -} - -/** - * Generate a new access token - */ -char* -OIDC_access_token_new () -{ - char* access_token_number; - char* access_token; - uint64_t random_number; - - random_number = GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - GNUNET_asprintf (&access_token_number, "%" PRIu64, random_number); - GNUNET_STRINGS_base64_encode(access_token_number,strlen(access_token_number),&access_token); - return access_token; -} diff --git a/src/rest-plugins/oidc_helper.h b/src/rest-plugins/oidc_helper.h deleted file mode 100644 index d718b7a78..000000000 --- a/src/rest-plugins/oidc_helper.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file reclaim/oidc_helper.h - * @brief helper library for OIDC related functions - * @author Martin Schanzenbach - */ - -#ifndef JWT_H -#define JWT_H - -#define JWT_ALG "alg" - -/* Use 512bit HMAC */ -#define JWT_ALG_VALUE "HS512" - -#define JWT_TYP "typ" - -#define JWT_TYP_VALUE "jwt" - -#define SERVER_ADDRESS "https://api.reclaim" - -/** - * Create a JWT from attributes - * - * @param aud_key the public of the audience - * @param sub_key the public key of the subject - * @param attrs the attribute list - * @param expiration_time the validity of the token - * @param secret_key the key used to sign the JWT - * @return a new base64-encoded JWT string. - */ -char* -OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, - const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, - const struct GNUNET_TIME_Relative *expiration_time, - const char *nonce, - const char *secret_key); - -/** - * Builds an OIDC authorization code including - * a reclaim ticket and nonce - * - * @param issuer the issuer of the ticket, used to sign the ticket and nonce - * @param ticket the ticket to include in the code - * @param nonce the nonce to include in the code - * @return a new authorization code (caller must free) - */ -char* -OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, - const struct GNUNET_RECLAIM_Ticket *ticket, - const char* nonce); - -/** - * Parse reclaim ticket and nonce from - * authorization code. - * This also verifies the signature in the code. - * - * @param audience the expected audience of the code - * @param code the string representation of the code - * @param ticket where to store the ticket - * @param nonce where to store the nonce - * @return GNUNET_OK if successful, else GNUNET_SYSERR - */ -int -OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, - const char* code, - struct GNUNET_RECLAIM_Ticket **ticket, - char **nonce); - -/** - * Build a token response for a token request - * TODO: Maybe we should add the scope here? - * - * @param access_token the access token to include - * @param id_token the id_token to include - * @param expiration_time the expiration time of the token(s) - * @param token_response where to store the response - */ -void -OIDC_build_token_response (const char *access_token, - const char *id_token, - const struct GNUNET_TIME_Relative *expiration_time, - char **token_response); -/** - * Generate a new access token - */ -char* -OIDC_access_token_new (); - - -#endif diff --git a/src/rest-plugins/plugin_rest_gns.c b/src/rest-plugins/plugin_rest_gns.c deleted file mode 100644 index e41df4085..000000000 --- a/src/rest-plugins/plugin_rest_gns.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @author Philippe Buschmann - * @file gns/plugin_rest_gns.c - * @brief GNUnet Gns REST plugin - */ - -#include "platform.h" -#include "gnunet_rest_plugin.h" -#include "gnunet_rest_lib.h" -#include "gnunet_json_lib.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_gns_service.h" -#include "microhttpd.h" -#include - -/** - * Rest API GNS Namespace - */ -#define GNUNET_REST_API_NS_GNS "/gns" - -/** - * Rest API GNS Parameter record_type - */ -#define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type" - -/** - * Rest API GNS ERROR Unknown Error - */ -#define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error" - -/** - * Rest API GNS ERROR Record not found - */ -#define GNUNET_REST_GNS_NOT_FOUND "Record not found" - -/** - * The configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * HTTP methods allows for this plugin - */ -static char* allow_methods; - -/** - * @brief struct returned by the initialization function of the plugin - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - -/** - * The request handle - */ -struct RequestHandle -{ - - /** - * Connection to GNS - */ - struct GNUNET_GNS_Handle *gns; - - /** - * Active GNS lookup - */ - struct GNUNET_GNS_LookupWithTldRequest *gns_lookup; - - /** - * Name to look up - */ - char *name; - - /** - * Record type to look up - */ - int record_type; - - /** - * Rest connection - */ - struct GNUNET_REST_RequestHandle *rest_handle; - - /** - * Desired timeout for the lookup (default is no timeout). - */ - struct GNUNET_TIME_Relative timeout; - - /** - * ID of a task associated with the resolution process. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The plugin result processor - */ - GNUNET_REST_ResultProcessor proc; - - /** - * The closure of the result processor - */ - void *proc_cls; - - /** - * The url - */ - char *url; - - /** - * Error response message - */ - char *emsg; - - /** - * Response code - */ - int response_code; - -}; - - -/** - * Cleanup lookup handle - * @param handle Handle to clean up - */ -static void -cleanup_handle (void *cls) -{ - struct RequestHandle *handle = cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up\n"); - - if (NULL != handle->gns_lookup) - { - GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup); - handle->gns_lookup = NULL; - } - if (NULL != handle->gns) - { - GNUNET_GNS_disconnect (handle->gns); - handle->gns = NULL; - } - - if (NULL != handle->timeout_task) - { - GNUNET_SCHEDULER_cancel (handle->timeout_task); - handle->timeout_task = NULL; - } - if (NULL != handle->url) - GNUNET_free (handle->url); - if (NULL != handle->name) - GNUNET_free (handle->name); - if (NULL != handle->emsg) - GNUNET_free (handle->emsg); - - GNUNET_free (handle); -} - - -/** - * Task run on errors. Reports an error and cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - json_t *json_error = json_object(); - char *response; - - if (NULL == handle->emsg) - handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN); - - json_object_set_new(json_error,"error", json_string(handle->emsg)); - - if (0 == handle->response_code) - handle->response_code = MHD_HTTP_OK; - response = json_dumps (json_error, 0); - resp = GNUNET_REST_create_response (response); - handle->proc (handle->proc_cls, resp, handle->response_code); - json_decref(json_error); - GNUNET_free(response); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -/** - * Iterator called on obtained result for a GNS lookup. - * - * @param cls closure with the object - * @param was_gns #GNUNET_NO if name was not a GNS name - * @param rd_count number of records in @a rd - * @param rd the records in reply - */ -static void -handle_gns_response (void *cls, - int was_gns, - uint32_t rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - json_t *result_array; - json_t *record_obj; - char *result; - - handle->gns_lookup = NULL; - - if (GNUNET_NO == was_gns) - { - handle->response_code = MHD_HTTP_NOT_FOUND; - handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - result_array = json_array(); - for (uint32_t i=0;irecord_type) && - (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) ) - { - continue; - } - - record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]); - json_array_append (result_array, record_obj); - json_decref (record_obj); - } - - result = json_dumps(result_array, 0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result); - resp = GNUNET_REST_create_response (result); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free (result); - json_decref (result_array); - GNUNET_SCHEDULER_add_now(&cleanup_handle, handle); -} - - -/** - * Handle gns GET request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_HashCode key; - char *record_type; - char *name; - - name = NULL; - handle->name = NULL; - if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url)) - { - name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1]; - } - - if (NULL == name) - { - handle->response_code = MHD_HTTP_NOT_FOUND; - handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (0 >= strlen (name)) - { - handle->response_code = MHD_HTTP_NOT_FOUND; - handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->name = GNUNET_strdup(name); - - handle->record_type = UINT32_MAX; - GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE, - strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE), - &key); - if ( GNUNET_YES - == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, - &key)) - { - record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key); - handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type); - } - - if(UINT32_MAX == handle->record_type) - { - handle->record_type = GNUNET_GNSRECORD_TYPE_ANY; - } - - handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns, - handle->name, - handle->record_type, - GNUNET_NO, - &handle_gns_response, - handle); -} - - - -/** - * Respond to OPTIONS request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -options_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - //independent of path return all options - resp = GNUNET_REST_create_response (NULL); - MHD_add_response_header (resp, - "Access-Control-Allow-Methods", - allow_methods); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_SCHEDULER_add_now(&cleanup_handle, handle); - return; -} - - -/** - * Handle rest request - * - * @param handle the request handle - */ -static void -init_cont (struct RequestHandle *handle) -{ - struct GNUNET_REST_RequestHandlerError err; - static const struct GNUNET_REST_RequestHandler handlers[] = { - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont}, - {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont}, - GNUNET_REST_HANDLER_END - }; - - if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, - handlers, - &err, - handle)) - { - handle->response_code = err.error_code; - GNUNET_SCHEDULER_add_now (&do_error, handle); - } -} - - -/** - * Function processing the REST call - * - * @param method HTTP method - * @param url URL of the HTTP request - * @param data body of the HTTP request (optional) - * @param data_size length of the body - * @param proc callback function for the result - * @param proc_cls closure for callback function - * @return GNUNET_OK if request accepted - */ -static void -rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle, - GNUNET_REST_ResultProcessor proc, - void *proc_cls) -{ - struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - - handle->response_code = 0; - handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60); - handle->proc_cls = proc_cls; - handle->proc = proc; - handle->rest_handle = rest_handle; - - handle->url = GNUNET_strdup (rest_handle->url); - if (handle->url[strlen (handle->url)-1] == '/') - handle->url[strlen (handle->url)-1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); - handle->gns = GNUNET_GNS_connect (cfg); - init_cont(handle); - - handle->timeout_task = - GNUNET_SCHEDULER_add_delayed (handle->timeout, - &do_error, - handle); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); -} - - -/** - * Entry point for the plugin. - * - * @param cls Config info - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_rest_gns_init (void *cls) -{ - static struct Plugin plugin; - struct GNUNET_REST_Plugin *api; - - cfg = cls; - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof (struct Plugin)); - plugin.cfg = cfg; - api = GNUNET_new (struct GNUNET_REST_Plugin); - api->cls = &plugin; - api->name = GNUNET_REST_API_NS_GNS; - api->process_request = &rest_process_request; - GNUNET_asprintf (&allow_methods, - "%s, %s, %s, %s, %s", - MHD_HTTP_METHOD_GET, - MHD_HTTP_METHOD_POST, - MHD_HTTP_METHOD_PUT, - MHD_HTTP_METHOD_DELETE, - MHD_HTTP_METHOD_OPTIONS); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Gns REST API initialized\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_rest_gns_done (void *cls) -{ - struct GNUNET_REST_Plugin *api = cls; - struct Plugin *plugin = api->cls; - plugin->cfg = NULL; - - GNUNET_free_non_null (allow_methods); - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Gns REST plugin is finished\n"); - return NULL; -} - -/* end of plugin_rest_gns.c */ - diff --git a/src/rest-plugins/plugin_rest_openid_connect.c b/src/rest-plugins/plugin_rest_openid_connect.c deleted file mode 100644 index e755f079b..000000000 --- a/src/rest-plugins/plugin_rest_openid_connect.c +++ /dev/null @@ -1,2330 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @author Martin Schanzenbach - * @author Philippe Buschmann - * @file identity/plugin_rest_openid_connect.c - * @brief GNUnet Namestore REST plugin - * - */ - -#include "platform.h" -#include "gnunet_rest_plugin.h" -#include "gnunet_identity_service.h" -#include "gnunet_gns_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_namestore_service.h" -#include "gnunet_rest_lib.h" -#include "microhttpd.h" -#include -#include -#include "gnunet_signatures.h" -#include "gnunet_reclaim_attribute_lib.h" -#include "gnunet_reclaim_service.h" -#include "oidc_helper.h" - -/** - * REST root namespace - */ -#define GNUNET_REST_API_NS_OIDC "/openid" - -/** - * Authorize endpoint - */ -#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize" - -/** - * Token endpoint - */ -#define GNUNET_REST_API_NS_TOKEN "/openid/token" - -/** - * UserInfo endpoint - */ -#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo" - -/** - * Login namespace - */ -#define GNUNET_REST_API_NS_LOGIN "/openid/login" - -/** - * State while collecting all egos - */ -#define ID_REST_STATE_INIT 0 - -/** - * Done collecting egos - */ -#define ID_REST_STATE_POST_INIT 1 - -/** - * OIDC grant_type key - */ -#define OIDC_GRANT_TYPE_KEY "grant_type" - -/** - * OIDC grant_type key - */ -#define OIDC_GRANT_TYPE_VALUE "authorization_code" - -/** - * OIDC code key - */ -#define OIDC_CODE_KEY "code" - -/** - * OIDC response_type key - */ -#define OIDC_RESPONSE_TYPE_KEY "response_type" - -/** - * OIDC client_id key - */ -#define OIDC_CLIENT_ID_KEY "client_id" - -/** - * OIDC scope key - */ -#define OIDC_SCOPE_KEY "scope" - -/** - * OIDC redirect_uri key - */ -#define OIDC_REDIRECT_URI_KEY "redirect_uri" - -/** - * OIDC state key - */ -#define OIDC_STATE_KEY "state" - -/** - * OIDC nonce key - */ -#define OIDC_NONCE_KEY "nonce" - -/** - * OIDC cookie expiration (in seconds) - */ -#define OIDC_COOKIE_EXPIRATION 3 - -/** - * OIDC cookie header key - */ -#define OIDC_COOKIE_HEADER_KEY "cookie" - -/** - * OIDC cookie header information key - */ -#define OIDC_AUTHORIZATION_HEADER_KEY "authorization" - -/** - * OIDC cookie header information key - */ -#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity=" - -/** - * OIDC cookie header if user cancelled - */ -#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied" - -/** - * OIDC expected response_type while authorizing - */ -#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code" - -/** - * OIDC expected scope part while authorizing - */ -#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid" - -/** - * OIDC error key for invalid client - */ -#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client" - -/** - * OIDC error key for invalid scopes - */ -#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope" - -/** - * OIDC error key for invalid requests - */ -#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request" - -/** - * OIDC error key for invalid tokens - */ -#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token" - -/** - * OIDC error key for invalid cookies - */ -#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie" - -/** - * OIDC error key for generic server errors - */ -#define OIDC_ERROR_KEY_SERVER_ERROR "server_error" - -/** - * OIDC error key for unsupported grants - */ -#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type" - -/** - * OIDC error key for unsupported response types - */ -#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type" - -/** - * OIDC error key for unauthorized clients - */ -#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client" - -/** - * OIDC error key for denied access - */ -#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" - - - -/** - * OIDC ignored parameter array - */ -static char* OIDC_ignored_parameter_array [] = -{ - "display", - "prompt", - "ui_locales", - "response_mode", - "id_token_hint", - "login_hint", - "acr_values" -}; - -/** - * OIDC Hash map that keeps track of issued cookies - */ -struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map; - -/** - * OIDC authorized identities and times hashmap - */ -struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants; - -/** - * OIDC Hash map that keeps track of used authorization code(s) - */ -struct GNUNET_CONTAINER_MultiHashMap *OIDC_used_ticket_map; - -/** - * Hash map that links the issued access token to the corresponding ticket and ego - */ -struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map; - -/** - * The configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * HTTP methods allows for this plugin - */ -static char* allow_methods; - -/** - * @brief struct returned by the initialization function of the plugin - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - -/** - * OIDC needed variables - */ -struct OIDC_Variables -{ - /** - * The RP client public key - */ - struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey; - - /** - * The OIDC client id of the RP - */ - char *client_id; - - /** - * The OIDC redirect uri - */ - char *redirect_uri; - - /** - * The list of oidc scopes - */ - char *scope; - - /** - * The OIDC state - */ - char *state; - - /** - * The OIDC nonce - */ - char *nonce; - - /** - * The OIDC response type - */ - char *response_type; - - /** - * The identity chosen by the user to login - */ - char *login_identity; - - /** - * User cancelled authorization/login - */ - int user_cancelled; - - /** - * The response JSON - */ - json_t *response; - -}; - -/** - * The ego list - */ -struct EgoEntry -{ - /** - * DLL - */ - struct EgoEntry *next; - - /** - * DLL - */ - struct EgoEntry *prev; - - /** - * Ego Identifier - */ - char *identifier; - - /** - * Public key string - */ - char *keystring; - - /** - * The Ego - */ - struct GNUNET_IDENTITY_Ego *ego; -}; - - -struct RequestHandle -{ - /** - * Ego list - */ - struct EgoEntry *ego_head; - - /** - * Ego list - */ - struct EgoEntry *ego_tail; - - /** - * Selected ego - */ - struct EgoEntry *ego_entry; - - /** - * Pointer to ego private key - */ - struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key; - - /** - * OIDC variables - */ - struct OIDC_Variables *oidc; - - /** - * The processing state - */ - int state; - - /** - * Handle to Identity service. - */ - struct GNUNET_IDENTITY_Handle *identity_handle; - - /** - * Rest connection - */ - struct GNUNET_REST_RequestHandle *rest_handle; - - /** - * GNS handle - */ - struct GNUNET_GNS_Handle *gns_handle; - - /** - * GNS lookup op - */ - struct GNUNET_GNS_LookupRequest *gns_op; - - /** - * Handle to NAMESTORE - */ - struct GNUNET_NAMESTORE_Handle *namestore_handle; - - /** - * Iterator for NAMESTORE - */ - struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it; - - /** - * Attribute claim list - */ - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list; - - /** - * IDENTITY Operation - */ - struct GNUNET_IDENTITY_Operation *op; - - /** - * Identity Provider - */ - struct GNUNET_RECLAIM_Handle *idp; - - /** - * Idp Operation - */ - struct GNUNET_RECLAIM_Operation *idp_op; - - /** - * Attribute iterator - */ - struct GNUNET_RECLAIM_AttributeIterator *attr_it; - - /** - * Ticket iterator - */ - struct GNUNET_RECLAIM_TicketIterator *ticket_it; - - /** - * A ticket - */ - struct GNUNET_RECLAIM_Ticket ticket; - - /** - * Desired timeout for the lookup (default is no timeout). - */ - struct GNUNET_TIME_Relative timeout; - - /** - * ID of a task associated with the resolution process. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The plugin result processor - */ - GNUNET_REST_ResultProcessor proc; - - /** - * The closure of the result processor - */ - void *proc_cls; - - /** - * The url - */ - char *url; - - /** - * The tld for redirect - */ - char *tld; - - /** - * The redirect prefix - */ - char *redirect_prefix; - - /** - * The redirect suffix - */ - char *redirect_suffix; - - /** - * Error response message - */ - char *emsg; - - /** - * Error response description - */ - char *edesc; - - /** - * Reponse code - */ - int response_code; - -}; - -/** - * Cleanup lookup handle - * @param handle Handle to clean up - */ -static void -cleanup_handle (struct RequestHandle *handle) -{ - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry; - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp; - struct EgoEntry *ego_entry; - struct EgoEntry *ego_tmp; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up\n"); - if (NULL != handle->timeout_task) - GNUNET_SCHEDULER_cancel (handle->timeout_task); - if (NULL != handle->identity_handle) - GNUNET_IDENTITY_disconnect (handle->identity_handle); - if (NULL != handle->attr_it) - GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); - if (NULL != handle->ticket_it) - GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); - if (NULL != handle->idp) - GNUNET_RECLAIM_disconnect (handle->idp); - GNUNET_free_non_null (handle->url); - GNUNET_free_non_null (handle->tld); - GNUNET_free_non_null (handle->redirect_prefix); - GNUNET_free_non_null (handle->redirect_suffix); - GNUNET_free_non_null (handle->emsg); - GNUNET_free_non_null (handle->edesc); - if (NULL != handle->gns_op) - GNUNET_GNS_lookup_cancel (handle->gns_op); - if (NULL != handle->gns_handle) - GNUNET_GNS_disconnect (handle->gns_handle); - - if (NULL != handle->namestore_handle) - GNUNET_NAMESTORE_disconnect (handle->namestore_handle); - if (NULL != handle->oidc) - { - GNUNET_free_non_null (handle->oidc->client_id); - GNUNET_free_non_null (handle->oidc->login_identity); - GNUNET_free_non_null (handle->oidc->nonce); - GNUNET_free_non_null (handle->oidc->redirect_uri); - GNUNET_free_non_null (handle->oidc->response_type); - GNUNET_free_non_null (handle->oidc->scope); - GNUNET_free_non_null (handle->oidc->state); - json_decref (handle->oidc->response); - GNUNET_free (handle->oidc); - } - if ( NULL != handle->attr_list ) - { - for (claim_entry = handle->attr_list->list_head; - NULL != claim_entry;) - { - claim_tmp = claim_entry; - claim_entry = claim_entry->next; - GNUNET_free (claim_tmp->claim); - GNUNET_free (claim_tmp); - } - GNUNET_free (handle->attr_list); - } - for (ego_entry = handle->ego_head; - NULL != ego_entry;) - { - ego_tmp = ego_entry; - ego_entry = ego_entry->next; - GNUNET_free (ego_tmp->identifier); - GNUNET_free (ego_tmp->keystring); - GNUNET_free (ego_tmp); - } - GNUNET_free_non_null (handle->attr_it); - GNUNET_free (handle); -} - -static void -cleanup_handle_delayed (void *cls) -{ - cleanup_handle (cls); -} - - -/** - * Task run on error, sends error message. Cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char *json_error; - - GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}", - handle->emsg, - (NULL != handle->edesc) ? handle->edesc : "", - (NULL != handle->oidc->state) ? ", \"state\":\"" : "", - (NULL != handle->oidc->state) ? handle->oidc->state : "", - (NULL != handle->oidc->state) ? "\"" : ""); - if ( 0 == handle->response_code ) - handle->response_code = MHD_HTTP_BAD_REQUEST; - resp = GNUNET_REST_create_response (json_error); - if (MHD_HTTP_UNAUTHORIZED == handle->response_code) - MHD_add_response_header (resp, - MHD_HTTP_HEADER_WWW_AUTHENTICATE, - "Basic"); - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json"); - handle->proc (handle->proc_cls, resp, handle->response_code); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (json_error); -} - - -/** - * Task run on error in userinfo endpoint, sends error header. Cleans up - * everything - * - * @param cls the `struct RequestHandle` - */ -static void -do_userinfo_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char *error; - - GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"", - handle->emsg, - (NULL != handle->edesc) ? handle->edesc : ""); - resp = GNUNET_REST_create_response (""); - MHD_add_response_header (resp, - MHD_HTTP_HEADER_WWW_AUTHENTICATE, - "Bearer"); - handle->proc (handle->proc_cls, resp, handle->response_code); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (error); -} - - -/** - * Task run on error, sends error message and redirects. Cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_redirect_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char* redirect; - GNUNET_asprintf (&redirect, - "%s?error=%s&error_description=%s%s%s", - handle->oidc->redirect_uri, handle->emsg, handle->edesc, - (NULL != handle->oidc->state) ? "&state=" : "", - (NULL != handle->oidc->state) ? handle->oidc->state : ""); - resp = GNUNET_REST_create_response (""); - MHD_add_response_header (resp, "Location", redirect); - handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (redirect); -} - -/** - * Task run on timeout, sends error message. Cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_timeout (void *cls) -{ - struct RequestHandle *handle = cls; - - handle->timeout_task = NULL; - do_error (handle); -} - -/** - * Return attributes for claim - * - * @param cls the request handle - */ -static void -return_userinfo_response (void *cls) -{ - char* result_str; - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - - result_str = json_dumps (handle->oidc->response, 0); - - resp = GNUNET_REST_create_response (result_str); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free (result_str); - cleanup_handle (handle); -} - -/** - * Returns base64 encoded string urlencoded - * - * @param string the string to encode - * @return base64 encoded string - */ -static char* -base64_encode (const char *s) -{ - char *enc; - char *enc_urlencode; - char *tmp; - int i; - int num_pads = 0; - - GNUNET_STRINGS_base64_encode (s, strlen (s), &enc); - tmp = strchr (enc, '='); - num_pads = strlen (enc) - (tmp - enc); - GNUNET_assert ((3 > num_pads) && (0 <= num_pads)); - if (0 == num_pads) - return enc; - enc_urlencode = GNUNET_malloc (strlen (enc) + num_pads*2); - strcpy (enc_urlencode, enc); - GNUNET_free (enc); - tmp = strchr (enc_urlencode, '='); - for (i = 0; i < num_pads; i++) - { - strcpy (tmp, "%3D"); //replace '=' with '%3D' - tmp += 3; - } - return enc_urlencode; -} - -/** - * Respond to OPTIONS request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -options_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - //For now, independent of path return all options - resp = GNUNET_REST_create_response (NULL); - MHD_add_response_header (resp, - "Access-Control-Allow-Methods", - allow_methods); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - cleanup_handle (handle); - return; -} - - -/** - * Interprets cookie header and pass its identity keystring to handle - */ -static void -cookie_identity_interpretation (struct RequestHandle *handle) -{ - struct GNUNET_HashCode cache_key; - char *cookies; - struct GNUNET_TIME_Absolute current_time, *relog_time; - char delimiter[] = "; "; - char *tmp_cookies; - char *token; - char *value; - - //gets identity of login try with cookie - GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY), - &cache_key); - if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, - &cache_key) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No cookie found\n"); - return; - } - //splits cookies and find 'Identity' cookie - tmp_cookies = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->header_param_map, &cache_key); - cookies = GNUNET_strdup (tmp_cookies); - token = strtok (cookies, delimiter); - handle->oidc->user_cancelled = GNUNET_NO; - handle->oidc->login_identity = NULL; - if (NULL == token) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse cookie: %s\n", - cookies); - GNUNET_free (cookies); - return; - } - - while (NULL != token) - { - if (0 == strcmp (token, - OIDC_COOKIE_HEADER_ACCESS_DENIED)) - { - handle->oidc->user_cancelled = GNUNET_YES; - GNUNET_free (cookies); - return; - } - if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY)) - break; - token = strtok (NULL, delimiter); - } - if (NULL == token) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No cookie value to process: %s\n", - cookies); - GNUNET_free (cookies); - return; - } - GNUNET_CRYPTO_hash (token, strlen (token), - &cache_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Found cookie `%s', but no corresponding expiration entry present...\n", - token); - GNUNET_free (cookies); - return; - } - relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, - &cache_key); - current_time = GNUNET_TIME_absolute_get (); - // 30 min after old login -> redirect to login - if ( current_time.abs_value_us > relog_time->abs_value_us ) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Found cookie `%s', but it is expired.\n", - token); - GNUNET_free (cookies); - return; - } - value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY); - GNUNET_assert (NULL != value); - handle->oidc->login_identity = GNUNET_strdup (value); -} - -/** - * Redirects to login page stored in configuration file - */ -static void -login_redirect (void *cls) -{ - char *login_base_url; - char *new_redirect; - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - if ( GNUNET_OK - == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin", - "address", &login_base_url) ) - { - GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", - login_base_url, - OIDC_RESPONSE_TYPE_KEY, - handle->oidc->response_type, - OIDC_CLIENT_ID_KEY, - handle->oidc->client_id, - OIDC_REDIRECT_URI_KEY, - handle->oidc->redirect_uri, - OIDC_SCOPE_KEY, - handle->oidc->scope, - OIDC_STATE_KEY, - (NULL != handle->oidc->state) ? handle->oidc->state : "", - OIDC_NONCE_KEY, - (NULL != handle->oidc->nonce) ? handle->oidc->nonce : ""); - resp = GNUNET_REST_create_response (""); - MHD_add_response_header (resp, "Location", new_redirect); - GNUNET_free (login_base_url); - } - else - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("gnunet configuration failed"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); - GNUNET_free (new_redirect); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - -/** - * Does internal server error when iteration failed. - */ -static void -oidc_iteration_error (void *cls) -{ - struct RequestHandle *handle = cls; - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); -} - - -/** - * Issues ticket and redirects to relying party with the authorization code as - * parameter. Otherwise redirects with error - */ -static void -oidc_ticket_issue_cb (void* cls, - const struct GNUNET_RECLAIM_Ticket *ticket) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char *ticket_str; - char *redirect_uri; - char *code_json_string; - char *code_base64_final_string; - - handle->idp_op = NULL; - handle->ticket = *ticket; - if (NULL == ticket) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("Server cannot generate ticket."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - ticket_str = GNUNET_STRINGS_data_to_string_alloc (&handle->ticket, - sizeof (struct GNUNET_RECLAIM_Ticket)); - //TODO change if more attributes are needed (see max_age) - code_json_string = OIDC_build_authz_code (&handle->priv_key, - &handle->ticket, - handle->oidc->nonce); - code_base64_final_string = base64_encode (code_json_string); - if ( (NULL != handle->redirect_prefix) && - (NULL != handle->redirect_suffix) && - (NULL != handle->tld) ) - { - - GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s", - handle->redirect_prefix, - handle->tld, - handle->redirect_suffix, - handle->oidc->response_type, - code_base64_final_string, handle->oidc->state); - } else { - GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s", - handle->oidc->redirect_uri, - handle->oidc->response_type, - code_base64_final_string, handle->oidc->state); - - } - resp = GNUNET_REST_create_response (""); - MHD_add_response_header (resp, "Location", redirect_uri); - handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (redirect_uri); - GNUNET_free (ticket_str); - GNUNET_free (code_json_string); - GNUNET_free (code_base64_final_string); -} - -static void -oidc_collect_finished_cb (void *cls) -{ - struct RequestHandle *handle = cls; - handle->attr_it = NULL; - handle->ticket_it = NULL; - if (NULL == handle->attr_list->list_head) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); - handle->edesc = GNUNET_strdup ("The requested scope is not available."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp, - &handle->priv_key, - &handle->oidc->client_pkey, - handle->attr_list, - &oidc_ticket_issue_cb, - handle); -} - - -/** - * Collects all attributes for an ego if in scope parameter - */ -static void -oidc_attr_collect (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) -{ - struct RequestHandle *handle = cls; - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; - char* scope_variables; - char* scope_variable; - char delimiter[]=" "; - - if ( (NULL == attr->name) || (NULL == attr->data) ) - { - GNUNET_RECLAIM_get_attributes_next (handle->attr_it); - return; - } - - scope_variables = GNUNET_strdup (handle->oidc->scope); - scope_variable = strtok (scope_variables, delimiter); - while (NULL != scope_variable) - { - if ( 0 == strcmp (attr->name, scope_variable) ) - { - break; - } - scope_variable = strtok (NULL, delimiter); - } - if ( NULL == scope_variable ) - { - GNUNET_RECLAIM_get_attributes_next (handle->attr_it); - GNUNET_free (scope_variables); - return; - } - GNUNET_free (scope_variables); - - le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry); - le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type, - attr->data, attr->data_size); - GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head, - handle->attr_list->list_tail, le); - GNUNET_RECLAIM_get_attributes_next (handle->attr_it); -} - - -/** - * Checks time and cookie and redirects accordingly - */ -static void -code_redirect (void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_TIME_Absolute current_time, *relog_time; - struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey; - struct GNUNET_HashCode cache_key; - char *identity_cookie; - - GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity); - GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key); - GNUNET_free (identity_cookie); - //No login time for identity -> redirect to login - if ( GNUNET_YES - == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, - &cache_key) ) - { - relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, - &cache_key); - current_time = GNUNET_TIME_absolute_get (); - // 30 min after old login -> redirect to login - if ( current_time.abs_value_us <= relog_time->abs_value_us ) - { - if ( GNUNET_OK - != GNUNET_CRYPTO_ecdsa_public_key_from_string ( - handle->oidc->login_identity, - strlen (handle->oidc->login_identity), &pubkey) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE); - handle->edesc = GNUNET_strdup ("The cookie of a login identity is not valid"); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - // iterate over egos and compare their public key - for (handle->ego_entry = handle->ego_head; - NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next) - { - GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey); - if ( 0 == memcmp (&ego_pkey, - &pubkey, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) ) - { - handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego); - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->attr_list = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); - handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp, - &handle->priv_key, - &oidc_iteration_error, - handle, - &oidc_attr_collect, - handle, - &oidc_collect_finished_cb, - handle); - return; - } - } - GNUNET_SCHEDULER_add_now (&login_redirect, handle); - return; - } - } -} - - -static void -build_redirect (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char* redirect_uri; - - if (GNUNET_YES == handle->oidc->user_cancelled) - { - if ( (NULL != handle->redirect_prefix) && - (NULL != handle->redirect_suffix) && - (NULL != handle->tld) ) - { - GNUNET_asprintf (&redirect_uri, "%s.%s/%s?error=%s&error_description=%s&state=%s", - handle->redirect_prefix, - handle->tld, - handle->redirect_suffix, - "access_denied", - "User denied access", - handle->oidc->state); - } else { - GNUNET_asprintf (&redirect_uri, "%s?error=%s&error_description=%s&state=%s", - handle->oidc->redirect_uri, - "access_denied", - "User denied access", - handle->oidc->state); - - } - resp = GNUNET_REST_create_response (""); - MHD_add_response_header (resp, "Location", redirect_uri); - handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (redirect_uri); - return; - } - GNUNET_SCHEDULER_add_now (&code_redirect, handle); -} - - -static void -lookup_redirect_uri_result (void *cls, - uint32_t rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - char *tmp; - char *tmp_key_str; - char *pos; - struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone; - - handle->gns_op = NULL; - if (0 == rd_count) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - for (int i = 0; i < rd_count; i++) - { - if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type) - continue; - if (0 != strncmp (rd[i].data, - handle->oidc->redirect_uri, - rd[i].data_size)) - continue; - tmp = GNUNET_strndup (rd[i].data, - rd[i].data_size); - if (NULL == strstr (tmp, - handle->oidc->client_id)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Redirect uri %s does not contain client_id %s", - tmp, - handle->oidc->client_id); - } else { - - pos = strrchr (tmp, - (unsigned char) '.'); - *pos = '\0'; - handle->redirect_prefix = GNUNET_strdup (tmp); - tmp_key_str = pos + 1; - pos = strchr (tmp_key_str, - (unsigned char) '/'); - *pos = '\0'; - handle->redirect_suffix = GNUNET_strdup (pos + 1); - - GNUNET_STRINGS_string_to_data (tmp_key_str, - strlen (tmp_key_str), - &redirect_zone, - sizeof (redirect_zone)); - } - GNUNET_SCHEDULER_add_now (&build_redirect, handle); - GNUNET_free (tmp); - return; - } - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("Server cannot generate ticket, redirect uri not found."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); -} - - - -/** - * Initiate redirect back to client. - */ -static void -client_redirect (void *cls) -{ - struct RequestHandle *handle = cls; - - /* Lookup client redirect uri to verify request */ - handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle, - GNUNET_GNS_EMPTY_LABEL_AT, - &handle->oidc->client_pkey, - GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT, - GNUNET_GNS_LO_DEFAULT, - &lookup_redirect_uri_result, - handle); - -} - - -/** - * Iteration over all results finished, build final - * response. - * - * @param cls the `struct RequestHandle` - */ -static void -build_authz_response (void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_HashCode cache_key; - - char *expected_scope; - char delimiter[]=" "; - int number_of_ignored_parameter, iterator; - - - // REQUIRED value: redirect_uri - GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY), - &cache_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc=GNUNET_strdup ("missing parameter redirect_uri"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->oidc->redirect_uri = - GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key)); - - // REQUIRED value: response_type - GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY), - &cache_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc=GNUNET_strdup ("missing parameter response_type"); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type); - - // REQUIRED value: scope - GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); - handle->edesc=GNUNET_strdup ("missing parameter scope"); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - handle->oidc->scope = GNUNET_strdup (handle->oidc->scope); - - //OPTIONAL value: nonce - GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce); - } - - //TODO check other values if needed - number_of_ignored_parameter = sizeof (OIDC_ignored_parameter_array) / sizeof (char *); - for (iterator = 0; iterator < number_of_ignored_parameter; iterator++) - { - GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator], - strlen (OIDC_ignored_parameter_array[iterator]), - &cache_key); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED); - GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s", - OIDC_ignored_parameter_array[iterator]); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - } - - // Checks if response_type is 'code' - if (0 != strcmp (handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE); - handle->edesc=GNUNET_strdup ("The authorization server does not support " - "obtaining this authorization code."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - return; - } - - // Checks if scope contains 'openid' - expected_scope = GNUNET_strdup (handle->oidc->scope); - char* test; - test = strtok (expected_scope, delimiter); - while (NULL != test) - { - if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) ) - break; - test = strtok (NULL, delimiter); - } - if (NULL == test) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE); - handle->edesc=GNUNET_strdup ("The requested scope is invalid, unknown, or " - "malformed."); - GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); - GNUNET_free (expected_scope); - return; - } - - GNUNET_free (expected_scope); - if ( (NULL == handle->oidc->login_identity) && - (GNUNET_NO == handle->oidc->user_cancelled)) - GNUNET_SCHEDULER_add_now (&login_redirect, handle); - else - GNUNET_SCHEDULER_add_now (&client_redirect, handle); -} - -/** - * Iterate over tlds in config - */ -static void -tld_iter (void *cls, - const char *section, - const char *option, - const char *value) -{ - struct RequestHandle *handle = cls; - struct GNUNET_CRYPTO_EcdsaPublicKey pkey; - - if (GNUNET_OK != - GNUNET_CRYPTO_ecdsa_public_key_from_string (value, - strlen (value), - &pkey)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping non key %s\n", - value); - return; - } - if (0 == memcmp (&pkey, &handle->oidc->client_pkey, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - handle->tld = GNUNET_strdup (option+1); -} - -/** - * Responds to authorization GET and url-encoded POST request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_HashCode cache_key; - struct EgoEntry *tmp_ego; - const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; - struct GNUNET_CRYPTO_EcdsaPublicKey pkey; - - cookie_identity_interpretation (handle); - - //RECOMMENDED value: state - REQUIRED for answers - GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->oidc->state = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - handle->oidc->state = GNUNET_strdup (handle->oidc->state); - } - - // REQUIRED value: client_id - GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY), - &cache_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("missing parameter client_id"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key)); - - if ( GNUNET_OK - != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id, - strlen (handle->oidc->client_id), - &handle->oidc->client_pkey) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT); - handle->edesc = GNUNET_strdup ("The client is not authorized to request an " - "authorization code using this method."); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - - if ( NULL == handle->ego_head ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("Egos are missing"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - handle->ego_entry = handle->ego_head; - handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego); - //If we know this identity, translated the corresponding TLD - //TODO: We might want to have a reverse lookup functionality for TLDs? - for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next) - { - priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego); - GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, - &pkey); - if (0 == memcmp (&pkey, &handle->oidc->client_pkey, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - { - handle->tld = GNUNET_strdup (tmp_ego->identifier); - handle->ego_entry = handle->ego_tail; - } - } - if (NULL == handle->tld) - GNUNET_CONFIGURATION_iterate_section_values (cfg, - "gns", - tld_iter, - handle); - if (NULL == handle->tld) - handle->tld = GNUNET_strdup (handle->oidc->client_id); - GNUNET_SCHEDULER_add_now (&build_authz_response, handle); -} - -/** - * Combines an identity with a login time and responds OK to login request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -login_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct MHD_Response *resp = GNUNET_REST_create_response (""); - struct RequestHandle *handle = cls; - struct GNUNET_HashCode cache_key; - struct GNUNET_TIME_Absolute *current_time; - struct GNUNET_TIME_Absolute *last_time; - char* cookie; - char* header_val; - json_t *root; - json_error_t error; - json_t *identity; - char term_data[handle->rest_handle->data_size+1]; - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size); - root = json_loads (term_data, JSON_DECODE_ANY, &error); - identity = json_object_get (root, "identity"); - if (!json_is_string (identity)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error parsing json string from %s\n", - term_data); - handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST); - json_decref (root); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - return; - } - GNUNET_asprintf (&cookie, - "Identity=%s", - json_string_value (identity)); - GNUNET_asprintf (&header_val, - "%s;Max-Age=%d", - cookie, - OIDC_COOKIE_EXPIRATION); - MHD_add_response_header (resp, "Set-Cookie", header_val); - MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST"); - GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key); - - if (0 != strcmp (json_string_value (identity), - "Denied")) - { - current_time = GNUNET_new (struct GNUNET_TIME_Absolute); - *current_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (), - OIDC_COOKIE_EXPIRATION)); - last_time = GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key); - GNUNET_free_non_null (last_time); - GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map, - &cache_key, - current_time, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); - } - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free (cookie); - GNUNET_free (header_val); - json_decref (root); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - -static int -check_authorization (struct RequestHandle *handle, - struct GNUNET_CRYPTO_EcdsaPublicKey *cid) -{ - struct GNUNET_HashCode cache_key; - char *authorization; - char *credentials; - char *basic_authorization; - char *client_id; - char *pass; - char *expected_pass; - int client_exists = GNUNET_NO; - - GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY, - strlen (OIDC_AUTHORIZATION_HEADER_KEY), - &cache_key); - if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, - &cache_key) ) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->edesc=GNUNET_strdup ("missing authorization"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map, - &cache_key); - - //split header in "Basic" and [content] - credentials = strtok (authorization, " "); - if (0 != strcmp ("Basic", credentials)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - credentials = strtok (NULL, " "); - if (NULL == credentials) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - GNUNET_STRINGS_base64_decode (credentials, - strlen (credentials), - (void**)&basic_authorization); - - if ( NULL == basic_authorization ) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - client_id = strtok (basic_authorization, ":"); - if ( NULL == client_id ) - { - GNUNET_free_non_null (basic_authorization); - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - pass = strtok (NULL, ":"); - if (NULL == pass) - { - GNUNET_free_non_null (basic_authorization); - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - - //check client password - if ( GNUNET_OK - == GNUNET_CONFIGURATION_get_value_string (cfg, - "reclaim-rest-plugin", - "psw", - &expected_pass) ) - { - if (0 != strcmp (expected_pass, pass)) - { - GNUNET_free_non_null (basic_authorization); - GNUNET_free (expected_pass); - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - GNUNET_free (expected_pass); - } - else - { - GNUNET_free_non_null (basic_authorization); - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("gnunet configuration failed"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return GNUNET_SYSERR; - } - - //check client_id - for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry; ) - { - if (0 == strcmp (handle->ego_entry->keystring, client_id)) - { - client_exists = GNUNET_YES; - break; - } - handle->ego_entry = handle->ego_entry->next; - } - if (GNUNET_NO == client_exists) - { - GNUNET_free_non_null (basic_authorization); - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - return GNUNET_SYSERR; - } - GNUNET_STRINGS_string_to_data (client_id, - strlen (client_id), - cid, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - - GNUNET_free (basic_authorization); - return GNUNET_OK; -} - -static int -ego_exists (struct RequestHandle *handle, - struct GNUNET_CRYPTO_EcdsaPublicKey *test_key) -{ - struct EgoEntry *ego_entry; - struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; - - for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) - { - GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key); - if (0 == memcmp (&pub_key, - test_key, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - { - break; - } - } - if (NULL == ego_entry) - return GNUNET_NO; - return GNUNET_YES; -} - -static void -store_ticket_reference (const struct RequestHandle *handle, - const char* access_token, - const struct GNUNET_RECLAIM_Ticket *ticket, - const struct GNUNET_CRYPTO_EcdsaPublicKey *cid) -{ - struct GNUNET_HashCode cache_key; - char *id_ticket_combination; - char *ticket_string; - char *client_id; - - GNUNET_CRYPTO_hash (access_token, - strlen (access_token), - &cache_key); - client_id = GNUNET_STRINGS_data_to_string_alloc (cid, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket, - sizeof (struct GNUNET_RECLAIM_Ticket)); - GNUNET_asprintf (&id_ticket_combination, - "%s;%s", - client_id, - ticket_string); - GNUNET_CONTAINER_multihashmap_put (OIDC_access_token_map, - &cache_key, - id_ticket_combination, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); - - GNUNET_free (client_id); - GNUNET_free (ticket_string); -} - -/** - * Responds to token url-encoded POST request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_TIME_Relative expiration_time; - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl; - struct GNUNET_RECLAIM_Ticket *ticket; - struct GNUNET_CRYPTO_EcdsaPublicKey cid; - struct GNUNET_HashCode cache_key; - struct MHD_Response *resp; - char *grant_type; - char *code; - char *json_response; - char *id_token; - char *access_token; - char *jwt_secret; - char *nonce; - int i = 1; - - /* - * Check Authorization - */ - if (GNUNET_SYSERR == check_authorization (handle, - &cid)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "OIDC authorization for token endpoint failed\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - /* - * Check parameter - */ - - //TODO Do not allow multiple equal parameter names - //REQUIRED grant_type - GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key); - if (GNUNET_NO == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("missing parameter grant_type"); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - - //REQUIRED code - GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key); - if (GNUNET_NO == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("missing parameter code"); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &cache_key); - - //REQUIRED redirect_uri - GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY), - &cache_key); - if (GNUNET_NO == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &cache_key) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("missing parameter redirect_uri"); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - //Check parameter grant_type == "authorization_code" - if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type)) - { - handle->emsg=GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - GNUNET_CRYPTO_hash (code, strlen (code), &cache_key); - if (GNUNET_SYSERR == - GNUNET_CONTAINER_multihashmap_put (OIDC_used_ticket_map, - &cache_key, - &i, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("Cannot use the same code more than once"); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - //decode code - if (GNUNET_OK != OIDC_parse_authz_code (&cid, - code, - &ticket, - &nonce)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("invalid code"); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - //create jwt - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - "reclaim-rest-plugin", - "expiration_time", - &expiration_time)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); - handle->edesc = GNUNET_strdup ("gnunet configuration failed"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - GNUNET_free (ticket); - return; - } - - - //TODO OPTIONAL acr,amr,azp - if (GNUNET_NO == ego_exists (handle, - &ticket->audience)) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("invalid code..."); - handle->response_code = MHD_HTTP_BAD_REQUEST; - GNUNET_SCHEDULER_add_now (&do_error, handle); - GNUNET_free (ticket); - } - if ( GNUNET_OK - != GNUNET_CONFIGURATION_get_value_string (cfg, - "reclaim-rest-plugin", - "jwt_secret", - &jwt_secret) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); - handle->edesc = GNUNET_strdup ("No signing secret configured!"); - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); - GNUNET_free (ticket); - return; - } - //TODO We should collect the attributes here. cl always empty - cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList); - id_token = OIDC_id_token_new (&ticket->audience, - &ticket->identity, - cl, - &expiration_time, - (NULL != nonce) ? nonce : NULL, - jwt_secret); - access_token = OIDC_access_token_new (); - OIDC_build_token_response (access_token, - id_token, - &expiration_time, - &json_response); - - store_ticket_reference (handle, - access_token, - ticket, - &cid); - resp = GNUNET_REST_create_response (json_response); - MHD_add_response_header (resp, "Cache-Control", "no-store"); - MHD_add_response_header (resp, "Pragma", "no-cache"); - MHD_add_response_header (resp, "Content-Type", "application/json"); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl); - GNUNET_free (access_token); - GNUNET_free (json_response); - GNUNET_free (ticket); - GNUNET_free (id_token); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - -/** - * Collects claims and stores them in handle - */ -static void -consume_ticket (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) -{ - struct RequestHandle *handle = cls; - char *tmp_value; - json_t *value; - - if (NULL == identity) - { - GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle); - return; - } - tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, - attr->data, - attr->data_size); - value = json_string (tmp_value); - json_object_set_new (handle->oidc->response, - attr->name, - value); - GNUNET_free (tmp_value); -} - -/** - * Responds to userinfo GET and url-encoded POST request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, void *cls) -{ - //TODO expiration time - struct RequestHandle *handle = cls; - char delimiter[] = " "; - char delimiter_db[] = ";"; - struct GNUNET_HashCode cache_key; - char *authorization, *authorization_type, *authorization_access_token; - char *client_ticket, *client, *ticket_str; - struct GNUNET_RECLAIM_Ticket *ticket; - - GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY, - strlen (OIDC_AUTHORIZATION_HEADER_KEY), - &cache_key); - if ( GNUNET_NO - == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map, - &cache_key) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("No Access Token"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - return; - } - authorization = GNUNET_CONTAINER_multihashmap_get ( - handle->rest_handle->header_param_map, &cache_key); - - //split header in "Bearer" and access_token - authorization = GNUNET_strdup (authorization); - authorization_type = strtok (authorization, delimiter); - if ( 0 != strcmp ("Bearer", authorization_type) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("No Access Token"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - return; - } - authorization_access_token = strtok (NULL, delimiter); - if ( NULL == authorization_access_token ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("No Access Token"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - return; - } - - GNUNET_CRYPTO_hash (authorization_access_token, - strlen (authorization_access_token), - &cache_key); - if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map, - &cache_key) ) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("The Access Token expired"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - return; - } - - client_ticket = GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, - &cache_key); - client_ticket = GNUNET_strdup (client_ticket); - client = strtok (client_ticket,delimiter_db); - if (NULL == client) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("The Access Token expired"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - GNUNET_free (client_ticket); - return; - } - handle->ego_entry = handle->ego_head; - for (; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next) - { - if (0 == strcmp (handle->ego_entry->keystring,client)) - break; - } - if (NULL == handle->ego_entry) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("The Access Token expired"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - GNUNET_free (client_ticket); - return; - } - ticket_str = strtok (NULL, delimiter_db); - if (NULL == ticket_str) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("The Access Token expired"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (authorization); - GNUNET_free (client_ticket); - return; - } - ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket); - if ( GNUNET_OK - != GNUNET_STRINGS_string_to_data (ticket_str, - strlen (ticket_str), - ticket, - sizeof (struct GNUNET_RECLAIM_Ticket))) - { - handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); - handle->edesc = GNUNET_strdup ("The Access Token expired"); - handle->response_code = MHD_HTTP_UNAUTHORIZED; - GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); - GNUNET_free (ticket); - GNUNET_free (authorization); - GNUNET_free (client_ticket); - return; - } - - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->oidc->response = json_object (); - json_object_set_new (handle->oidc->response, - "sub", - json_string (handle->ego_entry->keystring)); - handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp, - GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego), - ticket, - consume_ticket, - handle); - GNUNET_free (ticket); - GNUNET_free (authorization); - GNUNET_free (client_ticket); - -} - - -/** - * Handle rest request - * - * @param handle the request handle - */ -static void -init_cont (struct RequestHandle *handle) -{ - struct GNUNET_REST_RequestHandlerError err; - static const struct GNUNET_REST_RequestHandler handlers[] = { - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont}, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint }, - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, - {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, - &options_cont}, - GNUNET_REST_HANDLER_END - }; - - if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, - handlers, - &err, - handle)) - { - handle->response_code = err.error_code; - GNUNET_SCHEDULER_add_now (&do_error, handle); - } -} - -/** - * If listing is enabled, prints information about the egos. - * - * This function is initially called for all egos and then again - * whenever a ego's identifier changes or if it is deleted. At the - * end of the initial pass over all egos, the function is once called - * with 'NULL' for 'ego'. That does NOT mean that the callback won't - * be invoked in the future or that there was an error. - * - * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', - * this function is only called ONCE, and 'NULL' being passed in - * 'ego' does indicate an error (i.e. name is taken or no default - * value is known). If 'ego' is non-NULL and if '*ctx' - * is set in those callbacks, the value WILL be passed to a subsequent - * call to the identity callback of 'GNUNET_IDENTITY_connect' (if - * that one was not NULL). - * - * When an identity is renamed, this function is called with the - * (known) ego but the NEW identifier. - * - * When an identity is deleted, this function is called with the - * (known) ego and "NULL" for the 'identifier'. In this case, - * the 'ego' is henceforth invalid (and the 'ctx' should also be - * cleaned up). - * - * @param cls closure - * @param ego ego handle - * @param ctx context for application to store data for this ego - * (during the lifetime of this process, initially NULL) - * @param identifier identifier assigned by the user for this ego, - * NULL if the user just deleted the ego and it - * must thus no longer be used - */ -static void -list_ego (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *identifier) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_CRYPTO_EcdsaPublicKey pk; - - if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) - { - handle->state = ID_REST_STATE_POST_INIT; - init_cont (handle); - return; - } - if (ID_REST_STATE_INIT == handle->state) { - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->keystring = - GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (identifier); - GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, - handle->ego_tail, - ego_entry); - return; - } - /* Ego renamed or added */ - if (identifier != NULL) { - for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) { - if (ego_entry->ego == ego) { - /* Rename */ - GNUNET_free (ego_entry->identifier); - ego_entry->identifier = GNUNET_strdup (identifier); - break; - } - } - if (NULL == ego_entry) { - /* Add */ - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->keystring = - GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (identifier); - GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, - handle->ego_tail, - ego_entry); - } - } else { - /* Delete */ - for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) { - if (ego_entry->ego == ego) - break; - } - if (NULL != ego_entry) - GNUNET_CONTAINER_DLL_remove (handle->ego_head,handle->ego_tail, ego_entry); - } - -} - -static void -rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle, - GNUNET_REST_ResultProcessor proc, - void *proc_cls) -{ - struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - handle->oidc = GNUNET_new (struct OIDC_Variables); - if (NULL == OIDC_cookie_jar_map) - OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); - if (NULL == OIDC_identity_grants) - OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); - if (NULL == OIDC_used_ticket_map) - OIDC_used_ticket_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); - if (NULL == OIDC_access_token_map) - OIDC_access_token_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); - handle->response_code = 0; - handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - handle->proc_cls = proc_cls; - handle->proc = proc; - handle->state = ID_REST_STATE_INIT; - handle->rest_handle = rest_handle; - - handle->url = GNUNET_strdup (rest_handle->url); - if (handle->url[strlen (handle->url)-1] == '/') - handle->url[strlen (handle->url)-1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connecting...\n"); - handle->identity_handle = GNUNET_IDENTITY_connect (cfg, - &list_ego, - handle); - handle->gns_handle = GNUNET_GNS_connect (cfg); - handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg); - handle->timeout_task = - GNUNET_SCHEDULER_add_delayed (handle->timeout, - &do_timeout, - handle); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connected\n"); -} - -/** - * Entry point for the plugin. - * - * @param cls Config info - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_rest_openid_connect_init (void *cls) -{ - static struct Plugin plugin; - struct GNUNET_REST_Plugin *api; - - cfg = cls; - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof (struct Plugin)); - plugin.cfg = cfg; - api = GNUNET_new (struct GNUNET_REST_Plugin); - api->cls = &plugin; - api->name = GNUNET_REST_API_NS_OIDC; - api->process_request = &rest_identity_process_request; - GNUNET_asprintf (&allow_methods, - "%s, %s, %s, %s, %s", - MHD_HTTP_METHOD_GET, - MHD_HTTP_METHOD_POST, - MHD_HTTP_METHOD_PUT, - MHD_HTTP_METHOD_DELETE, - MHD_HTTP_METHOD_OPTIONS); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Identity Provider REST API initialized\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_rest_openid_connect_done (void *cls) -{ - struct GNUNET_REST_Plugin *api = cls; - struct Plugin *plugin = api->cls; - plugin->cfg = NULL; - - struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it; - void *value = NULL; - hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map); - while (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) - GNUNET_free_non_null (value); - GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map); - - hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants); - while (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) - GNUNET_free_non_null (value); - GNUNET_CONTAINER_multihashmap_destroy (OIDC_identity_grants); - - hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_used_ticket_map); - while (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) - GNUNET_free_non_null (value); - GNUNET_CONTAINER_multihashmap_destroy (OIDC_used_ticket_map); - - hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map); - while (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value)) - GNUNET_free_non_null (value); - GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map); - GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it); - GNUNET_free_non_null (allow_methods); - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Identity Provider REST plugin is finished\n"); - return NULL; -} - -/* end of plugin_rest_identity_provider.c */ diff --git a/src/rest-plugins/plugin_rest_reclaim.c b/src/rest-plugins/plugin_rest_reclaim.c deleted file mode 100644 index b36ed2bb6..000000000 --- a/src/rest-plugins/plugin_rest_reclaim.c +++ /dev/null @@ -1,1104 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @author Martin Schanzenbach - * @author Philippe Buschmann - * @file reclaim/plugin_rest_reclaim.c - * @brief GNUnet reclaim REST plugin - * - */ - -#include "platform.h" -#include "gnunet_rest_plugin.h" -#include "gnunet_identity_service.h" -#include "gnunet_gns_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_namestore_service.h" -#include "gnunet_rest_lib.h" -#include "microhttpd.h" -#include -#include -#include "gnunet_signatures.h" -#include "gnunet_reclaim_attribute_lib.h" -#include "gnunet_reclaim_service.h" -#include "json_reclaim.h" - -/** - * REST root namespace - */ -#define GNUNET_REST_API_NS_RECLAIM "/reclaim" - -/** - * Attribute namespace - */ -#define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes" - -/** - * Ticket namespace - */ -#define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets" - -/** - * Revoke namespace - */ -#define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke" - -/** - * Revoke namespace - */ -#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume" - -/** - * State while collecting all egos - */ -#define ID_REST_STATE_INIT 0 - -/** - * Done collecting egos - */ -#define ID_REST_STATE_POST_INIT 1 - -/** - * The configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * HTTP methods allows for this plugin - */ -static char* allow_methods; - -/** - * @brief struct returned by the initialization function of the plugin - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - -/** - * The ego list - */ -struct EgoEntry -{ - /** - * DLL - */ - struct EgoEntry *next; - - /** - * DLL - */ - struct EgoEntry *prev; - - /** - * Ego Identifier - */ - char *identifier; - - /** - * Public key string - */ - char *keystring; - - /** - * The Ego - */ - struct GNUNET_IDENTITY_Ego *ego; -}; - - -struct RequestHandle -{ - /** - * Ego list - */ - struct EgoEntry *ego_head; - - /** - * Ego list - */ - struct EgoEntry *ego_tail; - - /** - * Selected ego - */ - struct EgoEntry *ego_entry; - - /** - * Pointer to ego private key - */ - struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key; - - /** - * The processing state - */ - int state; - - /** - * Handle to Identity service. - */ - struct GNUNET_IDENTITY_Handle *identity_handle; - - /** - * Rest connection - */ - struct GNUNET_REST_RequestHandle *rest_handle; - - /** - * Handle to NAMESTORE - */ - struct GNUNET_NAMESTORE_Handle *namestore_handle; - - /** - * Iterator for NAMESTORE - */ - struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it; - - /** - * Attribute claim list - */ - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list; - - /** - * IDENTITY Operation - */ - struct GNUNET_IDENTITY_Operation *op; - - /** - * Identity Provider - */ - struct GNUNET_RECLAIM_Handle *idp; - - /** - * Idp Operation - */ - struct GNUNET_RECLAIM_Operation *idp_op; - - /** - * Attribute iterator - */ - struct GNUNET_RECLAIM_AttributeIterator *attr_it; - - /** - * Ticket iterator - */ - struct GNUNET_RECLAIM_TicketIterator *ticket_it; - - /** - * A ticket - */ - struct GNUNET_RECLAIM_Ticket ticket; - - /** - * Desired timeout for the lookup (default is no timeout). - */ - struct GNUNET_TIME_Relative timeout; - - /** - * ID of a task associated with the resolution process. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The plugin result processor - */ - GNUNET_REST_ResultProcessor proc; - - /** - * The closure of the result processor - */ - void *proc_cls; - - /** - * The url - */ - char *url; - - /** - * Error response message - */ - char *emsg; - - /** - * Reponse code - */ - int response_code; - - /** - * Response object - */ - json_t *resp_object; - -}; - -/** - * Cleanup lookup handle - * @param handle Handle to clean up - */ -static void -cleanup_handle (struct RequestHandle *handle) -{ - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry; - struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp; - struct EgoEntry *ego_entry; - struct EgoEntry *ego_tmp; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up\n"); - if (NULL != handle->resp_object) - json_decref (handle->resp_object); - if (NULL != handle->timeout_task) - GNUNET_SCHEDULER_cancel (handle->timeout_task); - if (NULL != handle->identity_handle) - GNUNET_IDENTITY_disconnect (handle->identity_handle); - if (NULL != handle->attr_it) - GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); - if (NULL != handle->ticket_it) - GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); - if (NULL != handle->idp) - GNUNET_RECLAIM_disconnect (handle->idp); - if (NULL != handle->url) - GNUNET_free (handle->url); - if (NULL != handle->emsg) - GNUNET_free (handle->emsg); - if (NULL != handle->namestore_handle) - GNUNET_NAMESTORE_disconnect (handle->namestore_handle); - if ( NULL != handle->attr_list ) - { - for (claim_entry = handle->attr_list->list_head; - NULL != claim_entry;) - { - claim_tmp = claim_entry; - claim_entry = claim_entry->next; - GNUNET_free(claim_tmp->claim); - GNUNET_free(claim_tmp); - } - GNUNET_free (handle->attr_list); - } - for (ego_entry = handle->ego_head; - NULL != ego_entry;) - { - ego_tmp = ego_entry; - ego_entry = ego_entry->next; - GNUNET_free (ego_tmp->identifier); - GNUNET_free (ego_tmp->keystring); - GNUNET_free (ego_tmp); - } - if (NULL != handle->attr_it) - { - GNUNET_free(handle->attr_it); - } - GNUNET_free (handle); -} - -static void -cleanup_handle_delayed (void *cls) -{ - cleanup_handle (cls); -} - - -/** - * Task run on error, sends error message. Cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - char *json_error; - - GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", - handle->emsg); - if ( 0 == handle->response_code ) - { - handle->response_code = MHD_HTTP_BAD_REQUEST; - } - resp = GNUNET_REST_create_response (json_error); - MHD_add_response_header (resp, "Content-Type", "application/json"); - handle->proc (handle->proc_cls, resp, handle->response_code); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - GNUNET_free (json_error); -} - - -/** - * Task run on timeout, sends error message. Cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_timeout (void *cls) -{ - struct RequestHandle *handle = cls; - - handle->timeout_task = NULL; - do_error (handle); -} - - -static void -collect_error_cb (void *cls) -{ - struct RequestHandle *handle = cls; - - do_error (handle); -} - -static void -finished_cont (void *cls, - int32_t success, - const char *emsg) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - - resp = GNUNET_REST_create_response (emsg); - if (GNUNET_OK != success) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - - -/** - * Return attributes for identity - * - * @param cls the request handle - */ -static void -return_response (void *cls) -{ - char* result_str; - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - - result_str = json_dumps (handle->resp_object, 0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); - resp = GNUNET_REST_create_response (result_str); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free (result_str); - cleanup_handle (handle); -} - -static void -collect_finished_cb (void *cls) -{ - struct RequestHandle *handle = cls; - //Done - handle->attr_it = NULL; - handle->ticket_it = NULL; - GNUNET_SCHEDULER_add_now (&return_response, handle); -} - - -/** - * Collect all attributes for an ego - * - */ -static void -ticket_collect (void *cls, - const struct GNUNET_RECLAIM_Ticket *ticket) -{ - json_t *json_resource; - struct RequestHandle *handle = cls; - json_t *value; - char* tmp; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n"); - tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, - sizeof (uint64_t)); - json_resource = json_object (); - GNUNET_free (tmp); - json_array_append (handle->resp_object, - json_resource); - - tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - value = json_string (tmp); - json_object_set_new (json_resource, - "issuer", - value); - GNUNET_free (tmp); - tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - value = json_string (tmp); - json_object_set_new (json_resource, - "audience", - value); - GNUNET_free (tmp); - tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, - sizeof (uint64_t)); - value = json_string (tmp); - json_object_set_new (json_resource, - "rnd", - value); - GNUNET_free (tmp); - GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it); -} - - - -/** - * List tickets for identity request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - char *identity; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n", - handle->url); - if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >= - strlen (handle->url)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1; - - for (ego_entry = handle->ego_head; - NULL != ego_entry; - ego_entry = ego_entry->next) - if (0 == strcmp (identity, ego_entry->identifier)) - break; - handle->resp_object = json_array (); - - if (NULL == ego_entry) - { - //Done - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", - identity); - GNUNET_SCHEDULER_add_now (&return_response, handle); - return; - } - priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->ticket_it = GNUNET_RECLAIM_ticket_iteration_start (handle->idp, - priv_key, - &collect_error_cb, - handle, - &ticket_collect, - handle, - &collect_finished_cb, - handle); -} - - -static void -add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; - const char* identity; - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attribute; - struct GNUNET_TIME_Relative exp; - char term_data[handle->rest_handle->data_size+1]; - json_t *data_json; - json_error_t err; - struct GNUNET_JSON_Specification attrspec[] = { - GNUNET_RECLAIM_JSON_spec_claim (&attribute), - GNUNET_JSON_spec_end() - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n", - handle->url); - if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= - strlen (handle->url)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1; - - for (ego_entry = handle->ego_head; - NULL != ego_entry; - ego_entry = ego_entry->next) - if (0 == strcmp (identity, ego_entry->identifier)) - break; - - if (NULL == ego_entry) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Identity unknown (%s)\n", identity); - return; - } - identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - - if (0 >= handle->rest_handle->data_size) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_json = json_loads (term_data, - JSON_DECODE_ANY, - &err); - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (data_json, attrspec, - NULL, NULL)); - json_decref (data_json); - if (NULL == attribute) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse attribute from %s\n", - term_data); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->idp = GNUNET_RECLAIM_connect (cfg); - exp = GNUNET_TIME_UNIT_HOURS; - handle->idp_op = GNUNET_RECLAIM_attribute_store (handle->idp, - identity_priv, - attribute, - &exp, - &finished_cont, - handle); - GNUNET_JSON_parse_free (attrspec); -} - - - -/** - * Collect all attributes for an ego - * - */ -static void -attr_collect (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) -{ - struct RequestHandle *handle = cls; - json_t *attr_obj; - const char* type; - char* tmp_value; - - if ((NULL == attr->name) || (NULL == attr->data)) - { - GNUNET_RECLAIM_get_attributes_next (handle->attr_it); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", - attr->name); - - tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, - attr->data, - attr->data_size); - - attr_obj = json_object (); - json_object_set_new (attr_obj, - "value", - json_string (tmp_value)); - json_object_set_new (attr_obj, - "name", - json_string (attr->name)); - type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type); - json_object_set_new (attr_obj, - "type", - json_string (type)); - json_array_append (handle->resp_object, - attr_obj); - json_decref (attr_obj); - GNUNET_free(tmp_value); - GNUNET_RECLAIM_get_attributes_next (handle->attr_it); -} - - - -/** - * List attributes for identity request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - char *identity; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n", - handle->url); - if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= - strlen (handle->url)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1; - - for (ego_entry = handle->ego_head; - NULL != ego_entry; - ego_entry = ego_entry->next) - if (0 == strcmp (identity, ego_entry->identifier)) - break; - handle->resp_object = json_array (); - - - if (NULL == ego_entry) - { - //Done - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", - identity); - GNUNET_SCHEDULER_add_now (&return_response, handle); - return; - } - priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp, - priv_key, - &collect_error_cb, - handle, - &attr_collect, - handle, - &collect_finished_cb, - handle); -} - - -static void -revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_RECLAIM_Ticket *ticket; - struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk; - char term_data[handle->rest_handle->data_size+1]; - json_t *data_json; - json_error_t err; - struct GNUNET_JSON_Specification tktspec[] = { - GNUNET_RECLAIM_JSON_spec_ticket (&ticket), - GNUNET_JSON_spec_end() - }; - - if (0 >= handle->rest_handle->data_size) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_json = json_loads (term_data, - JSON_DECODE_ANY, - &err); - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (data_json, tktspec, - NULL, NULL)); - json_decref (data_json); - if (NULL == ticket) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse ticket from %s\n", - term_data); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (GNUNET_OK != GNUNET_JSON_parse (data_json, - tktspec, - NULL, NULL)) - { - handle->emsg = GNUNET_strdup ("Not a ticket!\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - GNUNET_JSON_parse_free (tktspec); - json_decref (data_json); - return; - } - - for (ego_entry = handle->ego_head; - NULL != ego_entry; - ego_entry = ego_entry->next) - { - GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, - &tmp_pk); - if (0 == memcmp (&ticket->identity, - &tmp_pk, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - break; - } - if (NULL == ego_entry) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Identity unknown\n"); - GNUNET_JSON_parse_free (tktspec); - return; - } - identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->idp_op = GNUNET_RECLAIM_ticket_revoke (handle->idp, - identity_priv, - ticket, - &finished_cont, - handle); - GNUNET_JSON_parse_free (tktspec); -} - -static void -consume_cont (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr) -{ - struct RequestHandle *handle = cls; - char *val_str; - json_t *value; - - if (NULL == identity) - { - GNUNET_SCHEDULER_add_now (&return_response, handle); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", - attr->name); - val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, - attr->data, - attr->data_size); - if (NULL == val_str) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse value for: %s\n", - attr->name); - return; - } - value = json_string(val_str); - json_object_set_new (handle->resp_object, - attr->name, - value); - json_decref (value); - GNUNET_free (val_str); -} - -static void -consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_RECLAIM_Ticket *ticket; - struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk; - char term_data[handle->rest_handle->data_size+1]; - json_t *data_json; - json_error_t err; - struct GNUNET_JSON_Specification tktspec[] = { - GNUNET_RECLAIM_JSON_spec_ticket (&ticket), - GNUNET_JSON_spec_end () - }; - - if (0 >= handle->rest_handle->data_size) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_json = json_loads (term_data, - JSON_DECODE_ANY, - &err); - if (NULL == data_json) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse JSON Object from %s\n", - term_data); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (GNUNET_OK != GNUNET_JSON_parse (data_json, - tktspec, - NULL, NULL)) - { - handle->emsg = GNUNET_strdup ("Not a ticket!\n"); - GNUNET_SCHEDULER_add_now (&do_error, handle); - GNUNET_JSON_parse_free(tktspec); - json_decref (data_json); - return; - } - for (ego_entry = handle->ego_head; - NULL != ego_entry; - ego_entry = ego_entry->next) - { - GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, - &tmp_pk); - if (0 == memcmp (&ticket->audience, - &tmp_pk, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - break; - } - if (NULL == ego_entry) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Identity unknown\n"); - GNUNET_JSON_parse_free (tktspec); - return; - } - identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - handle->resp_object = json_object (); - handle->idp = GNUNET_RECLAIM_connect (cfg); - handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp, - identity_priv, - ticket, - &consume_cont, - handle); - GNUNET_JSON_parse_free (tktspec); -} - - - -/** - * Respond to OPTIONS request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -options_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char* url, - void *cls) -{ - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - //For now, independent of path return all options - resp = GNUNET_REST_create_response (NULL); - MHD_add_response_header (resp, - "Access-Control-Allow-Methods", - allow_methods); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - cleanup_handle (handle); - return; -} - -/** - * Handle rest request - * - * @param handle the request handle - */ -static void -init_cont (struct RequestHandle *handle) -{ - struct GNUNET_REST_RequestHandlerError err; - static const struct GNUNET_REST_RequestHandler handlers[] = { - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont}, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont}, - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont}, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont}, - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont}, - {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM, - &options_cont}, - GNUNET_REST_HANDLER_END - }; - - if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, - handlers, - &err, - handle)) - { - handle->response_code = err.error_code; - GNUNET_SCHEDULER_add_now (&do_error, handle); - } -} - -/** - * If listing is enabled, prints information about the egos. - * - * This function is initially called for all egos and then again - * whenever a ego's identifier changes or if it is deleted. At the - * end of the initial pass over all egos, the function is once called - * with 'NULL' for 'ego'. That does NOT mean that the callback won't - * be invoked in the future or that there was an error. - * - * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', - * this function is only called ONCE, and 'NULL' being passed in - * 'ego' does indicate an error (i.e. name is taken or no default - * value is known). If 'ego' is non-NULL and if '*ctx' - * is set in those callbacks, the value WILL be passed to a subsequent - * call to the identity callback of 'GNUNET_IDENTITY_connect' (if - * that one was not NULL). - * - * When an identity is renamed, this function is called with the - * (known) ego but the NEW identifier. - * - * When an identity is deleted, this function is called with the - * (known) ego and "NULL" for the 'identifier'. In this case, - * the 'ego' is henceforth invalid (and the 'ctx' should also be - * cleaned up). - * - * @param cls closure - * @param ego ego handle - * @param ctx context for application to store data for this ego - * (during the lifetime of this process, initially NULL) - * @param identifier identifier assigned by the user for this ego, - * NULL if the user just deleted the ego and it - * must thus no longer be used - */ -static void -list_ego (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *identifier) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_CRYPTO_EcdsaPublicKey pk; - - if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) - { - handle->state = ID_REST_STATE_POST_INIT; - init_cont (handle); - return; - } - if (ID_REST_STATE_INIT == handle->state) { - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->keystring = - GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (identifier); - GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry); - } - -} - -static void -rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle, - GNUNET_REST_ResultProcessor proc, - void *proc_cls) -{ - struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - handle->response_code = 0; - handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - handle->proc_cls = proc_cls; - handle->proc = proc; - handle->state = ID_REST_STATE_INIT; - handle->rest_handle = rest_handle; - - handle->url = GNUNET_strdup (rest_handle->url); - if (handle->url[strlen (handle->url)-1] == '/') - handle->url[strlen (handle->url)-1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connecting...\n"); - handle->identity_handle = GNUNET_IDENTITY_connect (cfg, - &list_ego, - handle); - handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg); - handle->timeout_task = - GNUNET_SCHEDULER_add_delayed (handle->timeout, - &do_timeout, - handle); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connected\n"); -} - -/** - * Entry point for the plugin. - * - * @param cls Config info - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_rest_reclaim_init (void *cls) -{ - static struct Plugin plugin; - struct GNUNET_REST_Plugin *api; - - cfg = cls; - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof (struct Plugin)); - plugin.cfg = cfg; - api = GNUNET_new (struct GNUNET_REST_Plugin); - api->cls = &plugin; - api->name = GNUNET_REST_API_NS_RECLAIM; - api->process_request = &rest_identity_process_request; - GNUNET_asprintf (&allow_methods, - "%s, %s, %s, %s, %s", - MHD_HTTP_METHOD_GET, - MHD_HTTP_METHOD_POST, - MHD_HTTP_METHOD_PUT, - MHD_HTTP_METHOD_DELETE, - MHD_HTTP_METHOD_OPTIONS); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Identity Provider REST API initialized\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_rest_reclaim_done (void *cls) -{ - struct GNUNET_REST_Plugin *api = cls; - struct Plugin *plugin = api->cls; - plugin->cfg = NULL; - - GNUNET_free_non_null (allow_methods); - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Identity Provider REST plugin is finished\n"); - return NULL; -} - -/* end of plugin_rest_reclaim.c */