/* This file is part of GNUnet. Copyright (C) 2012-2016 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 * @file credential/plugin_rest_credential.c * @brief GNUnet CREDENTIAL REST plugin * */ #include "platform.h" #include "gnunet_rest_plugin.h" #include #include #include #include #include #include #include #include #define GNUNET_REST_API_NS_CREDENTIAL "/credential" #define GNUNET_REST_API_NS_CREDENTIAL_ISSUE "/credential/issue" #define GNUNET_REST_API_NS_CREDENTIAL_VERIFY "/credential/verify" #define GNUNET_REST_API_NS_CREDENTIAL_COLLECT "/credential/collect" #define GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION "expiration" #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY "subject_key" #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO "subject" #define GNUNET_REST_JSONAPI_CREDENTIAL "credential" #define GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO "credential" #define GNUNET_REST_JSONAPI_DELEGATIONS "delegations" #define GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR "attribute" #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR "credential" /** * @brief struct returned by the initialization function of the plugin */ struct Plugin { const struct GNUNET_CONFIGURATION_Handle *cfg; }; const struct GNUNET_CONFIGURATION_Handle *cfg; struct RequestHandle { /** * Handle to Credential service. */ struct GNUNET_CREDENTIAL_Handle *credential; /** * Handle to lookup request */ struct GNUNET_CREDENTIAL_Request *verify_request; /** * Handle to issue request */ struct GNUNET_CREDENTIAL_Request *issue_request; /** * Handle to identity */ struct GNUNET_IDENTITY_Handle *identity; /** * Handle to identity operation */ struct GNUNET_IDENTITY_Operation *id_op; /** * Handle to ego lookup */ struct GNUNET_IDENTITY_EgoLookup *ego_lookup; /** * Handle to rest request */ struct GNUNET_REST_RequestHandle *rest_handle; /** * ID of a task associated with the resolution process. */ struct GNUNET_SCHEDULER_Task *timeout_task; /** * The root of the received JSON or NULL */ json_t *json_root; /** * The plugin result processor */ GNUNET_REST_ResultProcessor proc; /** * The closure of the result processor */ void *proc_cls; /** * The issuer attribute to verify */ char *issuer_attr; /** * The subject attribute */ char *subject_attr; /** * The public key of the issuer */ struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key; /** * The public key of the subject */ struct GNUNET_CRYPTO_EcdsaPublicKey subject_key; /** * HTTP response code */ int response_code; /** * Timeout */ struct GNUNET_TIME_Relative timeout; }; /** * Cleanup lookup handle. * * @param handle Handle to clean up */ static void cleanup_handle (struct RequestHandle *handle) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); if (NULL != handle->json_root) json_decref (handle->json_root); if (NULL != handle->issuer_attr) GNUNET_free (handle->issuer_attr); if (NULL != handle->subject_attr) GNUNET_free (handle->subject_attr); if (NULL != handle->verify_request) GNUNET_CREDENTIAL_request_cancel (handle->verify_request); if (NULL != handle->credential) GNUNET_CREDENTIAL_disconnect (handle->credential); if (NULL != handle->id_op) GNUNET_IDENTITY_cancel (handle->id_op); if (NULL != handle->ego_lookup) GNUNET_IDENTITY_ego_lookup_cancel (handle->ego_lookup); if (NULL != handle->identity) GNUNET_IDENTITY_disconnect (handle->identity); if (NULL != handle->timeout_task) { GNUNET_SCHEDULER_cancel (handle->timeout_task); } GNUNET_free (handle); } static void do_error (void *cls) { struct RequestHandle *handle = cls; struct MHD_Response *resp; resp = GNUNET_REST_create_response (NULL); handle->proc (handle->proc_cls, resp, handle->response_code); cleanup_handle (handle); } /** * Attribute delegation to JSON * * @param delegation_chain_entry the DSE * @return JSON, NULL if failed */ static json_t* attribute_delegation_to_json (struct GNUNET_CREDENTIAL_Delegation * delegation_chain_entry) { char *subject; char *issuer; json_t *attr_obj; issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string ( &delegation_chain_entry->issuer_key); if (NULL == issuer) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Issuer in delegation malformed\n"); return NULL; } subject = GNUNET_CRYPTO_ecdsa_public_key_to_string ( &delegation_chain_entry->subject_key); if (NULL == subject) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Subject in credential malformed\n"); GNUNET_free (issuer); return NULL; } attr_obj = json_object (); json_object_set_new (attr_obj, "issuer", json_string (issuer)); json_object_set_new (attr_obj, "issuer_attribute", json_string (delegation_chain_entry->issuer_attribute)); json_object_set_new (attr_obj, "subject", json_string (subject)); if (0 < delegation_chain_entry->subject_attribute_len) { json_object_set_new (attr_obj, "subject_attribute", json_string ( delegation_chain_entry->subject_attribute)); } GNUNET_free (issuer); GNUNET_free (subject); return attr_obj; } /** * JSONAPI resource to Credential * * @param res the JSONAPI resource * @return the resulting credential, NULL if failed */ static struct GNUNET_CREDENTIAL_Credential* json_to_credential (json_t *res) { struct GNUNET_CREDENTIAL_Credential *cred; json_t *tmp; const char *attribute; const char *signature; char *sig; tmp = json_object_get (res, "attribute"); if (0 == json_is_string (tmp)) { return NULL; } attribute = json_string_value (tmp); cred = GNUNET_malloc (sizeof(struct GNUNET_CREDENTIAL_Credential) + strlen (attribute)); cred->issuer_attribute = attribute; cred->issuer_attribute_len = strlen (attribute); tmp = json_object_get (res, "issuer"); if (0 == json_is_string (tmp)) { GNUNET_free (cred); return NULL; } GNUNET_CRYPTO_ecdsa_public_key_from_string (json_string_value (tmp), strlen (json_string_value (tmp)), &cred->issuer_key); tmp = json_object_get (res, "subject"); if (0 == json_is_string (tmp)) { GNUNET_free (cred); return NULL; } GNUNET_CRYPTO_ecdsa_public_key_from_string (json_string_value (tmp), strlen (json_string_value (tmp)), &cred->subject_key); tmp = json_object_get (res, "signature"); if (0 == json_is_string (tmp)) { GNUNET_free (cred); return NULL; } signature = json_string_value (tmp); GNUNET_STRINGS_base64_decode (signature, strlen (signature), (char**) &sig); GNUNET_memcpy (&cred->signature, sig, sizeof(struct GNUNET_CRYPTO_EcdsaSignature)); GNUNET_free (sig); tmp = json_object_get (res, "expiration"); if (0 == json_is_integer (tmp)) { GNUNET_free (cred); return NULL; } cred->expiration.abs_value_us = json_integer_value (tmp); return cred; } /** * Credential to JSON * * @param cred the credential * @return the resulting json, NULL if failed */ static json_t* credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred) { char *issuer; char *subject; char *signature; char attribute[cred->issuer_attribute_len + 1]; json_t *cred_obj; issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key); if (NULL == issuer) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Issuer in credential malformed\n"); return NULL; } subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key); if (NULL == subject) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Subject in credential malformed\n"); GNUNET_free (issuer); return NULL; } GNUNET_STRINGS_base64_encode ((char*) &cred->signature, sizeof(struct GNUNET_CRYPTO_EcdsaSignature), &signature); GNUNET_memcpy (attribute, cred->issuer_attribute, cred->issuer_attribute_len); attribute[cred->issuer_attribute_len] = '\0'; cred_obj = json_object (); json_object_set_new (cred_obj, "issuer", json_string (issuer)); json_object_set_new (cred_obj, "subject", json_string (subject)); json_object_set_new (cred_obj, "attribute", json_string (attribute)); json_object_set_new (cred_obj, "signature", json_string (signature)); json_object_set_new (cred_obj, "expiration", json_integer ( cred->expiration.abs_value_us)); GNUNET_free (issuer); GNUNET_free (subject); GNUNET_free (signature); return cred_obj; } static void handle_collect_response (void *cls, unsigned int d_count, struct GNUNET_CREDENTIAL_Delegation *delegation_chain, unsigned int c_count, struct GNUNET_CREDENTIAL_Credential *cred) { struct RequestHandle *handle = cls; struct MHD_Response *resp; struct GNUNET_JSONAPI_Document *json_document; struct GNUNET_JSONAPI_Resource *json_resource; json_t *cred_obj; json_t *cred_array; char *result; char *issuer; char *id; uint32_t i; handle->verify_request = NULL; if (NULL == cred) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Verify failed.\n"); handle->response_code = MHD_HTTP_NOT_FOUND; GNUNET_SCHEDULER_add_now (&do_error, handle); return; } issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&handle->issuer_key); if (NULL == issuer) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Issuer in delegation malformed\n"); return; } GNUNET_asprintf (&id, "%s.%s", issuer, handle->issuer_attr); GNUNET_free (issuer); json_document = GNUNET_JSONAPI_document_new (); json_resource = GNUNET_JSONAPI_resource_new ( GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO, id); GNUNET_free (id); cred_array = json_array (); for (i = 0; i < c_count; i++) { cred_obj = credential_to_json (&cred[i]); json_array_append_new (cred_array, cred_obj); } GNUNET_JSONAPI_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_CREDENTIAL, cred_array); GNUNET_JSONAPI_document_resource_add (json_document, json_resource); GNUNET_JSONAPI_document_serialize (json_document, &result); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result); json_decref (cred_array); GNUNET_JSONAPI_document_delete (json_document); resp = GNUNET_REST_create_response (result); GNUNET_free (result); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); cleanup_handle (handle); } static void subject_ego_lookup (void *cls, const struct GNUNET_IDENTITY_Ego *ego) { struct RequestHandle *handle = cls; const struct GNUNET_CRYPTO_EcdsaPrivateKey *sub_key; handle->ego_lookup = NULL; if (NULL == ego) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Subject not found\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } sub_key = GNUNET_IDENTITY_ego_get_private_key (ego); handle->verify_request = GNUNET_CREDENTIAL_collect (handle->credential, &handle->issuer_key, handle->issuer_attr, sub_key, &handle_collect_response, handle); } static void handle_verify_response (void *cls, unsigned int d_count, struct GNUNET_CREDENTIAL_Delegation *delegation_chain, unsigned int c_count, struct GNUNET_CREDENTIAL_Credential *cred) { struct RequestHandle *handle = cls; struct MHD_Response *resp; struct GNUNET_JSONAPI_Document *json_document; struct GNUNET_JSONAPI_Resource *json_resource; json_t *cred_obj; json_t *attr_obj; json_t *cred_array; json_t *attr_array; char *result; char *issuer; char *id; uint32_t i; handle->verify_request = NULL; if (NULL == cred) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Verify failed.\n"); handle->response_code = MHD_HTTP_NOT_FOUND; GNUNET_SCHEDULER_add_now (&do_error, handle); return; } issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&handle->issuer_key); if (NULL == issuer) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Issuer in delegation malformed\n"); return; } GNUNET_asprintf (&id, "%s.%s", issuer, handle->issuer_attr); GNUNET_free (issuer); json_document = GNUNET_JSONAPI_document_new (); json_resource = GNUNET_JSONAPI_resource_new ( GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO, id); GNUNET_free (id); attr_array = json_array (); for (i = 0; i < d_count; i++) { attr_obj = attribute_delegation_to_json (&delegation_chain[i]); json_array_append_new (attr_array, attr_obj); } cred_array = json_array (); for (i = 0; i < c_count; i++) { cred_obj = credential_to_json (&cred[i]); json_array_append_new (cred_array, cred_obj); } GNUNET_JSONAPI_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_CREDENTIAL, cred_array); GNUNET_JSONAPI_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_DELEGATIONS, attr_array); GNUNET_JSONAPI_document_resource_add (json_document, json_resource); GNUNET_JSONAPI_document_serialize (json_document, &result); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result); json_decref (attr_array); json_decref (cred_array); GNUNET_JSONAPI_document_delete (json_document); resp = GNUNET_REST_create_response (result); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); GNUNET_free (result); cleanup_handle (handle); } static void collect_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle, const char*url, void *cls) { struct RequestHandle *handle = cls; struct GNUNET_HashCode key; char *tmp; char *entity_attr; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); handle->credential = GNUNET_CREDENTIAL_connect (cfg); handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); if (NULL == handle->credential) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connecting to CREDENTIAL failed\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing issuer attribute\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key); entity_attr = GNUNET_strdup (tmp); tmp = strtok (entity_attr, "."); if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed issuer or attribute\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp, strlen (tmp), &handle->issuer_key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed issuer key\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = strtok (NULL, "."); // Issuer attribute if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed attribute\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } handle->issuer_attr = GNUNET_strdup (tmp); GNUNET_free (entity_attr); GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_EGO), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing subject\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key); if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed subject\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } handle->ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg, tmp, &subject_ego_lookup, handle); } static void verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle, const char*url, void *cls) { struct RequestHandle *handle = cls; struct GNUNET_HashCode key; struct GNUNET_JSONAPI_Document *json_obj; struct GNUNET_JSONAPI_Resource *res; struct GNUNET_CREDENTIAL_Credential *cred; char *tmp; char *entity_attr; int i; uint32_t credential_count; uint32_t resource_count; json_t *cred_json; json_t *data_js; json_error_t err; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); handle->credential = GNUNET_CREDENTIAL_connect (cfg); handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); if (NULL == handle->credential) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connecting to CREDENTIAL failed\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing issuer attribute\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key); entity_attr = GNUNET_strdup (tmp); tmp = strtok (entity_attr, "."); if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed issuer or attribute\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp, strlen (tmp), &handle->issuer_key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed issuer key\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = strtok (NULL, "."); // Issuer attribute if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed attribute\n"); GNUNET_free (entity_attr); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } handle->issuer_attr = GNUNET_strdup (tmp); GNUNET_free (entity_attr); GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing subject key\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map, &key); if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed subject\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp, strlen (tmp), &handle->subject_key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed subject key\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (0 >= handle->rest_handle->data_size) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing credentials\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } struct GNUNET_JSON_Specification docspec[] = { GNUNET_JSON_spec_jsonapi_document (&json_obj), GNUNET_JSON_spec_end () }; char term_data[handle->rest_handle->data_size + 1]; term_data[handle->rest_handle->data_size] = '\0'; credential_count = 0; GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size); data_js = json_loads (term_data, JSON_DECODE_ANY, &err); GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (data_js, docspec, NULL, NULL)); json_decref (data_js); if (NULL == json_obj) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse JSONAPI Object from %s\n", term_data); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } resource_count = GNUNET_JSONAPI_document_resource_count (json_obj); GNUNET_assert (1 == resource_count); res = (GNUNET_JSONAPI_document_get_resource (json_obj, 0)); if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (res, GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Resource not a credential!\n"); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse JSONAPI Object from %s\n", term_data); GNUNET_JSONAPI_document_delete (json_obj); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } cred_json = GNUNET_JSONAPI_resource_read_attr (res, GNUNET_REST_JSONAPI_CREDENTIAL); GNUNET_assert (json_is_array (cred_json)); credential_count = json_array_size (cred_json); struct GNUNET_CREDENTIAL_Credential credentials[credential_count]; for (i = 0; i < credential_count; i++) { cred = json_to_credential (json_array_get (cred_json, i)); if (NULL == cred) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse credential!\n"); continue; } GNUNET_memcpy (&credentials[i], cred, sizeof(struct GNUNET_CREDENTIAL_Credential)); credentials[i].issuer_attribute = GNUNET_strdup (cred->issuer_attribute); GNUNET_free (cred); } GNUNET_JSONAPI_document_delete (json_obj); handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential, &handle->issuer_key, handle->issuer_attr, &handle->subject_key, credential_count, credentials, &handle_verify_response, handle); for (i = 0; i < credential_count; i++) GNUNET_free ((char*) credentials[i].issuer_attribute); } void send_cred_response (struct RequestHandle *handle, struct GNUNET_CREDENTIAL_Credential *cred) { struct MHD_Response *resp; struct GNUNET_JSONAPI_Document *json_document; struct GNUNET_JSONAPI_Resource *json_resource; json_t *cred_obj; char *result; char *issuer; char *subject; char *signature; char *id; GNUNET_assert (NULL != cred); issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key); if (NULL == issuer) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Subject malformed\n"); GNUNET_free (issuer); return; } GNUNET_asprintf (&id, "%s.%s", issuer, (char*) &cred[1]); subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key); if (NULL == subject) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Subject malformed\n"); GNUNET_free (id); GNUNET_free (issuer); return; } GNUNET_STRINGS_base64_encode ((char*) &cred->signature, sizeof(struct GNUNET_CRYPTO_EcdsaSignature), &signature); json_document = GNUNET_JSONAPI_document_new (); json_resource = GNUNET_JSONAPI_resource_new ( GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO, id); GNUNET_free (id); cred_obj = json_object (); json_object_set_new (cred_obj, "issuer", json_string (issuer)); json_object_set_new (cred_obj, "subject", json_string (subject)); json_object_set_new (cred_obj, "expiration", json_integer ( cred->expiration.abs_value_us)); json_object_set_new (cred_obj, "signature", json_string (signature)); GNUNET_JSONAPI_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_CREDENTIAL, cred_obj); GNUNET_JSONAPI_document_resource_add (json_document, json_resource); GNUNET_JSONAPI_document_serialize (json_document, &result); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result); json_decref (cred_obj); GNUNET_JSONAPI_document_delete (json_document); resp = GNUNET_REST_create_response (result); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); GNUNET_free (result); GNUNET_free (signature); GNUNET_free (issuer); GNUNET_free (subject); cleanup_handle (handle); } void get_cred_issuer_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx, const char *name) { struct RequestHandle *handle = cls; struct GNUNET_TIME_Absolute etime_abs; struct GNUNET_TIME_Relative etime_rel; const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer_key; struct GNUNET_HashCode key; struct GNUNET_CREDENTIAL_Credential *cred; char*expiration_str; char*tmp; handle->id_op = NULL; if (NULL == name) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Issuer not configured!\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to credential service...\n"); handle->credential = GNUNET_CREDENTIAL_connect (cfg); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); if (NULL == handle->credential) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connecting to CREDENTIAL failed\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_EXPIRATION), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains ( handle->rest_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing expiration\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } expiration_str = GNUNET_CONTAINER_multihashmap_get ( handle->rest_handle->url_param_map, &key); if (NULL == expiration_str) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expiration malformed\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (GNUNET_OK == GNUNET_STRINGS_fancy_time_to_relative (expiration_str, &etime_rel)) { etime_abs = GNUNET_TIME_relative_to_absolute (etime_rel); } else if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (expiration_str, &etime_abs)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed expiration: %s\n", expiration_str); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains ( handle->rest_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing issuer attribute\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } handle->issuer_attr = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &key)); GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY, strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_KEY), &key); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains ( handle->rest_handle->url_param_map, &key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Missing subject\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } tmp = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &key); if (NULL == tmp) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed subject\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp, strlen (tmp), &handle->subject_key)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed subject key\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } issuer_key = GNUNET_IDENTITY_ego_get_private_key (ego); cred = GNUNET_CREDENTIAL_credential_issue (issuer_key, &handle->subject_key, handle->issuer_attr, &etime_abs); if (NULL == cred) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create credential\n"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } send_cred_response (handle, cred); } static void issue_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle, const char*url, void *cls) { struct RequestHandle *handle = cls; handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL); handle->id_op = GNUNET_IDENTITY_get (handle->identity, "credential-issuer", &get_cred_issuer_cb, handle); handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle); } static void options_cont (struct GNUNET_REST_RequestHandle *con_handle, const char*url, void *cls) { struct MHD_Response *resp; struct RequestHandle *handle = cls; // For GNS, independent of path return all options resp = GNUNET_REST_create_response (NULL); MHD_add_response_header (resp, "Access-Control-Allow-Methods", MHD_HTTP_METHOD_GET); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); cleanup_handle (handle); } static void rest_credential_process_request (struct GNUNET_REST_RequestHandle *conndata_handle, GNUNET_REST_ResultProcessor proc, void *proc_cls) { struct RequestHandle *handle = GNUNET_new (struct RequestHandle); struct GNUNET_REST_RequestHandlerError err; handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; handle->proc_cls = proc_cls; handle->proc = proc; handle->rest_handle = conndata_handle; static const struct GNUNET_REST_RequestHandler handlers[] = { { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_CREDENTIAL_VERIFY, &verify_cred_cont }, { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_COLLECT, &collect_cred_cont }, { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL_ISSUE, &issue_cred_cont }, { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont }, GNUNET_REST_HANDLER_END }; if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle, handlers, &err, handle)) { handle->response_code = err.error_code; GNUNET_SCHEDULER_add_now (&do_error, handle); } } /** * Entry point for the plugin. * * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" * @return NULL on error, otherwise the plugin context */ void * libgnunet_plugin_rest_credential_init (void *cls) { static struct Plugin plugin; cfg = cls; struct GNUNET_REST_Plugin *api; 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_CREDENTIAL; api->process_request = &rest_credential_process_request; GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("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_credential_done (void *cls) { struct GNUNET_REST_Plugin *api = cls; struct Plugin *plugin = api->cls; plugin->cfg = NULL; GNUNET_free (api); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNS REST plugin is finished\n"); return NULL; } /* end of plugin_rest_gns.c */