diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/anastasis-httpd.c | 22 | ||||
-rw-r--r-- | src/backend/anastasis-httpd_policy-meta.c | 195 | ||||
-rw-r--r-- | src/backend/anastasis-httpd_policy-meta.h | 41 | ||||
-rw-r--r-- | src/backend/anastasis-httpd_policy_upload.c | 41 | ||||
-rw-r--r-- | src/include/anastasis_crypto_lib.h | 6 | ||||
-rw-r--r-- | src/include/anastasis_service.h | 90 | ||||
-rw-r--r-- | src/restclient/Makefile.am | 2 | ||||
-rw-r--r-- | src/restclient/anastasis_api_policy_meta_lookup.c | 267 |
8 files changed, 654 insertions, 10 deletions
diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c index e2b363c..4ef6087 100644 --- a/src/backend/anastasis-httpd.c +++ b/src/backend/anastasis-httpd.c @@ -361,12 +361,15 @@ url_handler (void *cls, strlen ("/policy/"))) { const char *account = url + strlen ("/policy/"); + const char *end = strchr (account, '/'); struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub; if (GNUNET_OK != GNUNET_STRINGS_string_to_data ( account, - strlen (account), + (NULL == end) + ? strlen (account) + : end - account, &account_pub, sizeof (struct ANASTASIS_CRYPTO_AccountPublicKeyP))) { @@ -375,14 +378,23 @@ url_handler (void *cls, TALER_EC_GENERIC_PARAMETER_MALFORMED, "account public key"); } + if ( (NULL != end) && + (0 != strcmp (end, + "/meta")) ) + return TMH_MHD_handler_static_response (&h404, + connection); if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) { - return AH_policy_get (connection, - &account_pub); + if (NULL == end) + return AH_policy_get (connection, + &account_pub); + return AH_policy_meta_get (connection, + &account_pub); } - if (0 == strcmp (method, - MHD_HTTP_METHOD_POST)) + if ( (0 == strcmp (method, + MHD_HTTP_METHOD_POST)) && + (NULL == end) ) { return AH_handler_policy_post (connection, hc, diff --git a/src/backend/anastasis-httpd_policy-meta.c b/src/backend/anastasis-httpd_policy-meta.c new file mode 100644 index 0000000..a786c68 --- /dev/null +++ b/src/backend/anastasis-httpd_policy-meta.c @@ -0,0 +1,195 @@ +/* + This file is part of Anastasis + Copyright (C) 2022 Anastasis SARL + + Anastasis 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, or (at your option) any later version. + + Anastasis 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 + Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file anastasis-httpd_policy-meta.c + * @brief functions to handle incoming requests on /policy/$PID/meta + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-httpd.h" +#include "anastasis-httpd_policy-meta.h" +#include "anastasis_service.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_rest_lib.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_merchant_service.h> +#include <taler/taler_signatures.h> + + +/** + * Function called on matching meta data. Note that if the client did + * not provide meta data for @a version, the function will be called + * with @a recovery_meta_data being NULL. + * + * @param cls closure with a `json_t *` to build up + * @param version the version of the recovery document + * @param recovery_meta_data contains meta data about the encrypted recovery document + * @param recovery_meta_data_size size of @a recovery_meta_data blob + * @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration + */ +static enum GNUNET_GenericReturnValue +build_meta_result (void *cls, + uint32_t version, + const void *recovery_meta_data, + size_t recovery_meta_data_size) +{ + json_t *result = cls; + char version_s[14]; + + GNUNET_snprintf (version_s, + sizeof (version_s), + "%u", + (unsigned int) version); + GNUNET_assert (0 == + json_object_set_new ( + result, + version_s, + (NULL == recovery_meta_data) + ? json_null () + : GNUNET_JSON_from_data ( + recovery_meta_data, + recovery_meta_data_size))); + return GNUNET_OK; +} + + +/** + * Return the meta data on recovery documents of @a account on @a + * connection. + * + * @param connection MHD connection to use + * @param account_pub account to query + * @return MHD result code + */ +static MHD_RESULT +return_policy_meta ( + struct MHD_Connection *connection, + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) +{ + enum GNUNET_DB_QueryStatus qs; + uint32_t max_version; + json_t *result; + + { + const char *version_s; + + version_s = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "max_version"); + if (NULL != version_s) + { + char dummy; + + if (1 != sscanf (version_s, + "%u%c", + &max_version, + &dummy)) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "version"); + } + } + } + result = json_object (); + GNUNET_assert (NULL != result); + qs = db->get_recovery_meta_data (db->cls, + account_pub, + max_version, + &build_meta_result, + result); + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "get_recovery_document"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_SOFT_FAILURE, + "get_recovery_document"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_ANASTASIS_POLICY_NOT_FOUND, + NULL); + default: + /* interesting case below */ + break; + } + + return TALER_MHD_reply_json_steal (connection, + result, + MHD_HTTP_OK); +} + + +MHD_RESULT +AH_policy_meta_get ( + struct MHD_Connection *connection, + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub) +{ + struct GNUNET_HashCode recovery_data_hash; + enum ANASTASIS_DB_AccountStatus as; + MHD_RESULT ret; + uint32_t version; + struct GNUNET_TIME_Timestamp expiration; + + as = db->lookup_account (db->cls, + account_pub, + &expiration, + &recovery_data_hash, + &version); + switch (as) + { + case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_SYNC_ACCOUNT_UNKNOWN, + NULL); + case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup account"); + case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: + { + struct MHD_Response *resp; + + resp = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + TALER_MHD_add_global_headers (resp); + ret = MHD_queue_response (connection, + MHD_HTTP_NO_CONTENT, + resp); + MHD_destroy_response (resp); + } + return ret; + case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: + /* We have results, should fetch and return them! */ + break; + } + return return_policy_meta (connection, + account_pub); +} diff --git a/src/backend/anastasis-httpd_policy-meta.h b/src/backend/anastasis-httpd_policy-meta.h new file mode 100644 index 0000000..8c48fc6 --- /dev/null +++ b/src/backend/anastasis-httpd_policy-meta.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2022 Anastasis SARL + + Anastasis 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, or (at your option) any later version. + + Anastasis 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 + Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file anastasis-httpd_policy-meta.h + * @brief functions to handle incoming requests on /policy/ + * @author Dennis Neufeld + * @author Dominik Meister + * @author Christian Grothoff + */ +#ifndef ANASTASIS_HTTPD_POLICY_META_H +#define ANASTASIS_HTTPD_POLICY_META_H +#include <microhttpd.h> + + +/** + * Handle GET /policy/$ACCOUNT_PUB/meta request. + * + * @param connection the MHD connection to handle + * @param account_pub public key of the account + * @return MHD result code + */ +MHD_RESULT +AH_policy_meta_get ( + struct MHD_Connection *connection, + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub); + + +#endif diff --git a/src/backend/anastasis-httpd_policy_upload.c b/src/backend/anastasis-httpd_policy_upload.c index b63a82e..2cc0389 100644 --- a/src/backend/anastasis-httpd_policy_upload.c +++ b/src/backend/anastasis-httpd_policy_upload.c @@ -86,6 +86,16 @@ struct PolicyUploadContext char *upload; /** + * Meta data uploaded by the client, or NULL for none. + */ + void *meta_data; + + /** + * Number of bytes in @e meta_data. + */ + size_t meta_data_size; + + /** * Used while we are awaiting proposal creation. */ struct TALER_MERCHANT_PostOrdersHandle *po; @@ -220,6 +230,7 @@ cleanup_ctx (struct TM_HandlerContext *hc) if (NULL != puc->resp) MHD_destroy_response (puc->resp); GNUNET_free (puc->upload); + GNUNET_free (puc->meta_data); GNUNET_free (puc); } @@ -680,6 +691,32 @@ AH_handler_policy_post ( } } puc->account = *account_pub; + + /* check for meta-data */ + { + const char *metas; + + metas = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + ANASTASIS_HTTP_HEADER_POLICY_META_DATA); + if (NULL != metas) + { + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data_alloc (metas, + strlen (metas), + &puc->meta_data, + &puc->meta_data_size)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, + ANASTASIS_HTTP_HEADER_POLICY_META_DATA + " header must include a base32-encoded value"); + } + } + } /* now setup 'puc' */ { const char *lens; @@ -1121,8 +1158,8 @@ AH_handler_policy_post ( &puc->new_policy_upload_hash, puc->upload, puc->upload_size, - NULL, /* FIXME: meta-data! */ - 0, + puc->meta_data, + puc->meta_data_size, &puc->payment_identifier, &version); GNUNET_snprintf (version_s, diff --git a/src/include/anastasis_crypto_lib.h b/src/include/anastasis_crypto_lib.h index 780fb34..c28b83a 100644 --- a/src/include/anastasis_crypto_lib.h +++ b/src/include/anastasis_crypto_lib.h @@ -41,6 +41,12 @@ "Anastasis-Truth-Decryption-Key" /** + * Client to server: please store this meta data. + */ +#define ANASTASIS_HTTP_HEADER_POLICY_META_DATA "Anastasis-Policy-Meta-Data" + + +/** * Client to server: I paid using this payment secret. */ #define ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER "Anastasis-Payment-Identifier" diff --git a/src/include/anastasis_service.h b/src/include/anastasis_service.h index 0ef31d6..5a8ca21 100644 --- a/src/include/anastasis_service.h +++ b/src/include/anastasis_service.h @@ -156,6 +156,93 @@ ANASTASIS_config_cancel (struct ANASTASIS_ConfigOperation *co); /** + * Detailed meta data result. + */ +struct ANASTASIS_MetaDataEntry +{ + + /** + * The encrypted meta data we downloaded. + */ + const void *meta_data; + + /** + * Number of bytes in @e meta_data. + */ + size_t meta_data_size; + + /** + * Policy version this @e meta_data is for. + */ + uint32_t version; +}; + + +/** + * Detailed results for meta data download. + */ +struct ANASTASIS_MetaDownloadDetails +{ + + /** + * Version-sorted array of meta data we downloaded. + */ + const struct ANASTASIS_MetaDataEntry *metas; + + /** + * Number of entries in @e metas. + */ + unsigned int metas_length; + +}; + + +/** + * Callback to process a GET /policy/$POL/meta request + * + * @param cls closure + * @param http_status HTTP status code for this request + * @param dd the response details + */ +typedef void +(*ANASTASIS_PolicyMetaLookupCallback) ( + void *cls, + unsigned int http_status, + const struct ANASTASIS_MetaDownloadDetails *dd); + + +/** + * Does a GET /policy/$POL/meta. + * + * @param ctx execution context + * @param backend_url base URL of the merchant backend + * @param anastasis_pub public key of the user's account + * @param max_version maximum version number to fetch + * @param cb callback which will work the response gotten from the backend + * @param cb_cls closure to pass to the callback + * @return handle for this operation, NULL upon errors + */ +struct ANASTASIS_PolicyMetaLookupOperation * +ANASTASIS_policy_meta_lookup ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, + uint32_t max_version, + ANASTASIS_PolicyMetaLookupCallback cb, + void *cb_cls); + + +/** + * Cancel a GET /policy/$POL/meta request. + * + * @param plo cancel the policy lookup operation + */ +void +ANASTASIS_policy_meta_lookup_cancel ( + struct ANASTASIS_PolicyMetaLookupOperation *plo); + + +/** * Detailed results from the successful download. */ struct ANASTASIS_DownloadDetails @@ -198,8 +285,7 @@ struct ANASTASIS_PolicyLookupOperation; * * @param cls closure * @param http_status HTTP status code for this request - * @param ec anastasis-specific error code - * @param obj the response body + * @param dd the response details */ typedef void (*ANASTASIS_PolicyLookupCallback) (void *cls, diff --git a/src/restclient/Makefile.am b/src/restclient/Makefile.am index 075d3a7..19bf64c 100644 --- a/src/restclient/Makefile.am +++ b/src/restclient/Makefile.am @@ -19,6 +19,7 @@ libanastasisrest_la_SOURCES = \ anastasis_api_policy_store.c \ anastasis_api_truth_store.c \ anastasis_api_policy_lookup.c \ + anastasis_api_policy_meta_lookup.c \ anastasis_api_keyshare_lookup.c \ anastasis_api_curl_defaults.c anastasis_api_curl_defaults.h libanastasisrest_la_LIBADD = \ @@ -39,4 +40,3 @@ if HAVE_LIBGNURL libanastasisrest_la_LIBADD += -lgnurl endif endif - diff --git a/src/restclient/anastasis_api_policy_meta_lookup.c b/src/restclient/anastasis_api_policy_meta_lookup.c new file mode 100644 index 0000000..9be49ca --- /dev/null +++ b/src/restclient/anastasis_api_policy_meta_lookup.c @@ -0,0 +1,267 @@ +/* + This file is part of ANASTASIS + Copyright (C) 2022 Anastasis SARL + + ANASTASIS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + ANASTASIS 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 General Public License for more details. + + You should have received a copy of the GNU General Public + License along with ANASTASIS; see the file COPYING.LGPL. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file restclient/anastasis_api_policy_meta_lookup.c + * @brief Implementation of the /policy/$POL/meta GET request + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include "anastasis_service.h" +#include "anastasis_api_curl_defaults.h" +#include <taler/taler_signatures.h> + + +/** + * @brief A Meta Operation Handle + */ +struct ANASTASIS_PolicyMetaLookupOperation +{ + + /** + * The url for this request, including parameters. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + ANASTASIS_PolicyMetaLookupCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Public key of the account we are downloading from. + */ + struct ANASTASIS_CRYPTO_AccountPublicKeyP account_pub; + + /** + * Maximum version to fetch. + */ + uint32_t max_version; + +}; + + +/** + * Process GET /policy/$POL/meta response + * + * @param cls our `struct ANASTASIS_PolicyMetaLookupOperation *` + * @param response_code HTTP status + * @param data response body, a `json_t *`, NULL on error + */ +static void +handle_policy_meta_lookup_finished (void *cls, + long response_code, + const void *response) +{ + struct ANASTASIS_PolicyMetaLookupOperation *plo = cls; + const json_t *json = response; + + plo->job = NULL; + switch (response_code) + { + case 0: + /* Hard error */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Backend didn't even return from GET /policy\n"); + break; + case MHD_HTTP_OK: + { + size_t mlen = json_object_size (json); + + /* put a cap, as we will stack-allocate below and the + current service LIMITs the result to 1000 anyway; + could theoretically be increased in the future, but + then we should not put this onto the stack anymore... */ + if (mlen > 10000) + { + GNUNET_break (0); + response_code = 0; + break; + } + { + struct ANASTASIS_MetaDataEntry metas[GNUNET_NZL (mlen)]; + void *md[GNUNET_NZL (mlen)]; + struct ANASTASIS_MetaDownloadDetails mdd = { + .metas = metas, + .metas_length = mlen + }; + size_t off = 0; + const char *label; + const json_t *val; + + memset (md, + 0, + sizeof (md)); + json_object_foreach ((json_t *) json, + label, + val) + { + unsigned int ver; + char dummy; + const char *vals; + + if (1 != sscanf (label, + "%u%c", + &ver, + &dummy)) + { + GNUNET_break (0); + break; + } + metas[off].version = (uint32_t) ver; + if (json_is_null (val)) + { + metas[off].meta_data = NULL; + metas[off].meta_data_size = 0; + off++; + continue; + } + vals = json_string_value (val); + if ( (NULL == vals) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data_alloc (vals, + strlen (vals), + &md[off], + &metas[off].meta_data_size)) ) + { + GNUNET_break (0); + break; + } + metas[off].version = (uint32_t) ver; + metas[off].meta_data = md[off]; + off++; + } + if (off < mlen) + { + GNUNET_break (0); + response_code = 0; + for (size_t i = 0; i<off; i++) + GNUNET_free (md[i]); + break; + } + plo->cb (plo->cb_cls, + response_code, + &mdd); + for (size_t i = 0; i<off; i++) + GNUNET_free (md[i]); + plo->cb = NULL; + } + ANASTASIS_policy_meta_lookup_cancel (plo); + return; + } + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the anastasis server is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + (unsigned int) response_code); + GNUNET_break (0); + response_code = 0; + break; + } + plo->cb (plo->cb_cls, + response_code, + NULL); + plo->cb = NULL; + ANASTASIS_policy_meta_lookup_cancel (plo); +} + + +struct ANASTASIS_PolicyMetaLookupOperation * +ANASTASIS_policy_meta_lookup ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, + uint32_t max_version, + ANASTASIS_PolicyMetaLookupCallback cb, + void *cb_cls) +{ + struct ANASTASIS_PolicyMetaLookupOperation *plo; + CURL *eh; + char *path; + + GNUNET_assert (NULL != cb); + plo = GNUNET_new (struct ANASTASIS_PolicyMetaLookupOperation); + plo->account_pub = *anastasis_pub; + { + char *acc_pub_str; + + acc_pub_str = GNUNET_STRINGS_data_to_string_alloc (anastasis_pub, + sizeof (*anastasis_pub)); + GNUNET_asprintf (&path, + "policy/%s/meta", + acc_pub_str); + GNUNET_free (acc_pub_str); + } + plo->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + eh = ANASTASIS_curl_easy_get_ (plo->url); + GNUNET_assert (NULL != eh); + plo->cb = cb; + plo->cb_cls = cb_cls; + plo->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_policy_meta_lookup_finished, + plo); + return plo; +} + + +void +ANASTASIS_policy_meta_lookup_cancel (struct + ANASTASIS_PolicyMetaLookupOperation *plo) +{ + if (NULL != plo->job) + { + GNUNET_CURL_job_cancel (plo->job); + plo->job = NULL; + } + GNUNET_free (plo->url); + GNUNET_free (plo); +} |