commit dffa7bd9e4d1938ffc2e2c3fb5a4714aad5fb648
parent e0db82e03074aa3db0e75f66c64c66b224dedf96
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 26 Sep 2025 15:36:10 +0200
fix #10456
Diffstat:
13 files changed, 176 insertions(+), 54 deletions(-)
diff --git a/contrib/gana-generate.sh b/contrib/gana-generate.sh
@@ -13,7 +13,7 @@ if [ ! -f $SRC_ROOT/contrib/gana-generate.sh ]; then
fi
COMMIT_HASH=""
-if [ ! -z $1 ]; then
+if [ ! -z "${1:-}" ]; then
COMMIT_HASH=$1
fi
diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c
@@ -408,22 +408,43 @@ proceed_with_handler (struct DH_RequestContext *rc,
/**
- * Handle a "/charities/$CHARITY_ID" GET request.
+ * Handle a "/charities" GET request.
*
* @param rc request context
- * @param optional charity id (args[0])
+ * @param args empty array
* @return MHD result code
*/
static MHD_RESULT
handle_get_charities (struct DH_RequestContext *rc,
- const char *const args[1])
+ const char *const args[])
{
- if (NULL == args[0])
- {
- return DH_handler_charities_get (rc);
- }
+ GNUNET_break (NULL == args[0]);
+ return DH_handler_charities_get (rc);
+}
+
+
+/**
+ * Handle a "/charities/$CHARITY_ID" GET request.
+ *
+ * @param rc request context
+ * @param args charity id (in args[0])
+ * @return MHD result code
+ */
+static MHD_RESULT
+handle_get_charity_id (struct DH_RequestContext *rc,
+ const char *const args[1])
+{
+ struct DONAU_CharitySignatureP charity_sig;
+ bool sig_required = true;
+
+ TALER_MHD_parse_request_header_auto (
+ rc->connection,
+ DONAU_HTTP_HEADER_CHARITY_SIGNATURE,
+ &charity_sig,
+ sig_required);
return DH_handler_charity_get (rc,
- &args[0]);
+ &charity_sig,
+ args[0]);
}
@@ -480,10 +501,15 @@ handle_mhd_request (void *cls,
.url = "charities",
.method = MHD_HTTP_METHOD_GET,
.handler.get = &handle_get_charities,
- .nargs = 1,
- .nargs_is_upper_bound = true,
.needs_authorization = true
},
+ /* GET charity/$ID */
+ {
+ .url = "charity",
+ .method = MHD_HTTP_METHOD_GET,
+ .handler.get = &handle_get_charity_id,
+ .nargs = 1,
+ },
/* POST charities */
{
.url = "charities",
diff --git a/src/donau/donau-httpd_charity.h b/src/donau/donau-httpd_charity.h
@@ -44,13 +44,16 @@ DH_handler_charity_post (
* Handle a GET "/charities/$CHARITY_ID" request.
*
* @param rc request context
- * @param args GET arguments (should be one)
+ * @param charity_sig signature of the charity authorizing access
+ * (to be checked!)
+ * @param charity_id_s the "$CHARITY_ID" argument
* @return MHD result code
*/
MHD_RESULT
DH_handler_charity_get (
struct DH_RequestContext *rc,
- const char *const args[1]);
+ const struct DONAU_CharitySignatureP *charity_sig,
+ const char *charity_id_s);
/**
diff --git a/src/donau/donau-httpd_charity_get.c b/src/donau/donau-httpd_charity_get.c
@@ -35,16 +35,18 @@
*/
#define MAX_RECORDS 1024
+
MHD_RESULT
DH_handler_charity_get (
struct DH_RequestContext *rc,
- const char *const args[1])
+ const struct DONAU_CharitySignatureP *charity_sig,
+ const char *charity_id_s)
{
unsigned long long charity_id;
char dummy;
- if ( (NULL == args[0]) ||
- (1 != sscanf (args[0],
+ if ( (NULL == charity_id_s) ||
+ (1 != sscanf (charity_id_s,
"%llu%c",
&charity_id,
&dummy)) )
@@ -78,12 +80,22 @@ DH_handler_charity_get (
rc->connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_DONAU_CHARITY_NOT_FOUND,
- args[0]);
+ charity_id_s);
break;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
+ if (GNUNET_OK !=
+ DONAU_charity_get_info_verify (&meta.charity_pub,
+ charity_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_GENERIC_FORBIDDEN,
+ DONAU_HTTP_HEADER_CHARITY_SIGNATURE);
+ }
result = TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_OK,
diff --git a/src/include/donau_crypto_lib.h b/src/include/donau_crypto_lib.h
@@ -441,6 +441,32 @@ DONAU_charity_bkp_verify (
const struct DONAU_CharitySignatureP *charity_sig);
+/**
+ * Create charity eddsa signature approving a request for charity
+ * status information.
+ *
+ * @param charity_priv private key of the charity
+ * @param[out] charity_sig where to write the signature
+ */
+void
+DONAU_charity_get_info_sign (
+ const struct DONAU_CharityPrivateKeyP *charity_priv,
+ struct DONAU_CharitySignatureP *charity_sig);
+
+
+/**
+ * Verify charity eddsa signature requesting charity information.
+ *
+ * @param charity_pub public key of the charity
+ * @param charity_sig where to write the signature
+ * @return #GNUNET_OK if the signature is valid
+ */
+enum GNUNET_GenericReturnValue
+DONAU_charity_get_info_verify (
+ const struct DONAU_CharityPublicKeyP *charity_pub,
+ const struct DONAU_CharitySignatureP *charity_sig);
+
+
/* ********************* donau eddsa signing ************************** */
/**
diff --git a/src/include/donau_service.h b/src/include/donau_service.h
@@ -1151,8 +1151,8 @@ typedef void
*
* @param ctx curl context
* @param url donau base URL
- * @param bearer for authorization
* @param id of the requested charity
+ * @param charity_priv private key of the charity, for authorization
* @param cb the callback to call when a reply for this request is available
* @param cb_cls closure for the above callback
* @return a handle for this request; NULL if the inputs are invalid (i.e.
@@ -1162,8 +1162,8 @@ struct DONAU_CharityGetHandle *
DONAU_charity_get (
struct GNUNET_CURL_Context *ctx,
const char *url,
- const uint64_t id,
- const struct DONAU_BearerToken *bearer,
+ uint64_t id,
+ const struct DONAU_CharityPrivateKeyP *charity_priv,
DONAU_GetCharityResponseCallback cb,
void *cb_cls);
diff --git a/src/include/donau_signatures.h b/src/include/donau_signatures.h
@@ -38,4 +38,10 @@
#define DONAU_SIGNATURE_CHARITY_DONATION_CONFIRMATION 1501
+/**
+ * The signature is made by a charity to request information about its status from a Donau. It is not over anything in particular and is just there for access control.
+ */
+#define DONAU_SIGNATURE_CHARITY_GET_INFO 1502
+
+
#endif
diff --git a/src/include/donau_testing_lib.h b/src/include/donau_testing_lib.h
@@ -47,19 +47,18 @@ TALER_TESTING_cmd_get_donau (
const struct GNUNET_CONFIGURATION_Handle *cfg,
bool wait_for_keys);
+
/**
* Create a GET "charity" command.
*
* @param label the command label.
* @param charity_reference reference for traits
- * @param bearer authorization token
* @param expected_response_code expected HTTP response code.
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_charity_get (const char *label,
const char *charity_reference,
- const struct DONAU_BearerToken *bearer,
unsigned int expected_response_code);
/**
@@ -203,24 +202,24 @@ TALER_TESTING_get_donau_url (
* Call #op on all simple traits.
*/
#define DONAU_TESTING_SIMPLE_TRAITS(op) \
- op (charity_priv, const struct DONAU_CharityPrivateKeyP) \
- op (charity_pub, const struct DONAU_CharityPublicKeyP) \
- op (charity_id, const uint64_t) \
- op (donau_url, const char) \
- op (donau_keys, struct DONAU_Keys) \
- op (donor_salt, const char) \
- op (donor_tax_id, const char) \
- op (salted_tax_id_hash, const struct DONAU_HashDonorTaxId) \
- op (donation_receipts, const struct DONAU_DonationReceipt*) \
- op (number_receipts, const size_t)
+ op (charity_priv, const struct DONAU_CharityPrivateKeyP) \
+ op (charity_pub, const struct DONAU_CharityPublicKeyP) \
+ op (charity_id, const uint64_t) \
+ op (donau_url, const char) \
+ op (donau_keys, struct DONAU_Keys) \
+ op (donor_salt, const char) \
+ op (donor_tax_id, const char) \
+ op (salted_tax_id_hash, const struct DONAU_HashDonorTaxId) \
+ op (donation_receipts, const struct DONAU_DonationReceipt*) \
+ op (number_receipts, const size_t)
/**
* Call #op on all indexed traits.
*/
#define DONAU_TESTING_INDEXED_TRAITS(op) \
- op (donation_unit_pub, const struct DONAU_DonationUnitInformation) \
- op (donau_pub, const struct TALER_ExchangePublicKeyP)
+ op (donation_unit_pub, const struct DONAU_DonationUnitInformation) \
+ op (donau_pub, const struct TALER_ExchangePublicKeyP)
DONAU_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_DECL_SIMPLE_TRAIT)
DONAU_TESTING_INDEXED_TRAITS (TALER_TESTING_MAKE_DECL_INDEXED_TRAIT)
diff --git a/src/include/donau_util.h b/src/include/donau_util.h
@@ -30,6 +30,11 @@
#include "donau_crypto_lib.h"
/**
+ * HTTP header with a signature by the charity.
+ */
+#define DONAU_HTTP_HEADER_CHARITY_SIGNATURE "Charity-Signature"
+
+/**
* Return default project data used by Taler.
*/
const struct GNUNET_OS_ProjectData *
diff --git a/src/lib/donau_api_charity_get.c b/src/lib/donau_api_charity_get.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2024 Taler Systems SA
+ Copyright (C) 2024, 2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -194,7 +194,7 @@ DONAU_charity_get (
struct GNUNET_CURL_Context *ctx,
const char *url,
const uint64_t id,
- const struct DONAU_BearerToken *bearer, // for authorization
+ const struct DONAU_CharityPrivateKeyP *charity_priv,
DONAU_GetCharityResponseCallback cb,
void *cb_cls)
{
@@ -211,7 +211,7 @@ DONAU_charity_get (
cgh->cb_cls = cb_cls;
GNUNET_snprintf (arg_str,
sizeof (arg_str),
- "charities/%llu",
+ "charity/%llu",
(unsigned long long)
id);
cgh->url = TALER_url_join (url,
@@ -238,15 +238,23 @@ DONAU_charity_get (
&handle_charity_get_finished,
cgh);
GNUNET_assert (NULL != cgh->job);
- if (NULL != bearer)
{
- struct curl_slist *auth;
+ struct DONAU_CharitySignatureP charity_sig;
+ char *sig_hdr;
char *hdr;
+ struct curl_slist *auth;
+ DONAU_charity_get_info_sign (charity_priv,
+ &charity_sig);
+
+ sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
+ &charity_sig,
+ sizeof (charity_sig));
GNUNET_asprintf (&hdr,
- "%s: Bearer %s",
- MHD_HTTP_HEADER_AUTHORIZATION,
- bearer->token);
+ "%s: %s",
+ DONAU_HTTP_HEADER_CHARITY_SIGNATURE,
+ sig_hdr);
+ GNUNET_free (sig_hdr);
auth = curl_slist_append (NULL,
hdr);
GNUNET_free (hdr);
@@ -254,6 +262,7 @@ DONAU_charity_get (
auth);
curl_slist_free_all (auth);
}
+
return cgh;
}
diff --git a/src/testing/test_donau_api.c b/src/testing/test_donau_api.c
@@ -81,7 +81,6 @@ run (void *cls,
MHD_HTTP_CREATED),
TALER_TESTING_cmd_charity_get ("get-charity-by-id",
"post-charity", // cmd trait reference
- &bearer,
MHD_HTTP_OK),
TALER_TESTING_cmd_charities_get ("get-charities",
&bearer,
diff --git a/src/testing/testing_api_cmd_charity_get.c b/src/testing/testing_api_cmd_charity_get.c
@@ -40,11 +40,6 @@ struct StatusState
struct DONAU_CharityGetHandle *cgh;
/**
- * The bearer token for authorization.
- */
- const struct DONAU_BearerToken *bearer;
-
- /**
* The ID of the requested charity.
*/
uint64_t charity_id;
@@ -110,6 +105,7 @@ status_run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct StatusState *ss = cls;
+ const struct DONAU_CharityPrivateKeyP *charity_priv;
(void) cmd;
ss->is = is;
@@ -118,9 +114,10 @@ status_run (void *cls,
const struct TALER_TESTING_Command *charity_post_cmd;
const uint64_t *charity_id;
- charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is,
- ss->
- charity_reference);
+ charity_post_cmd
+ = TALER_TESTING_interpreter_lookup_command (
+ is,
+ ss->charity_reference);
if (GNUNET_OK !=
TALER_TESTING_get_trait_charity_id (charity_post_cmd,
@@ -130,6 +127,14 @@ status_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_charity_priv (charity_post_cmd,
+ &charity_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
ss->charity_id = *(charity_id);
}
@@ -137,7 +142,7 @@ status_run (void *cls,
TALER_TESTING_interpreter_get_context (is),
TALER_TESTING_get_donau_url (is),
ss->charity_id,
- ss->bearer,
+ charity_priv,
&charity_status_cb,
ss);
}
@@ -171,13 +176,11 @@ status_cleanup (void *cls,
struct TALER_TESTING_Command
TALER_TESTING_cmd_charity_get (const char *label,
const char *charity_reference,
- const struct DONAU_BearerToken *bearer,
unsigned int expected_response_code)
{
struct StatusState *ss;
ss = GNUNET_new (struct StatusState);
ss->expected_response_code = expected_response_code;
- ss->bearer = bearer;
ss->charity_reference = charity_reference;
{
struct TALER_TESTING_Command cmd = {
diff --git a/src/util/charity_signatures.c b/src/util/charity_signatures.c
@@ -157,4 +157,38 @@ DONAU_charity_bkp_verify (
}
+enum GNUNET_GenericReturnValue
+DONAU_charity_get_info_verify (
+ const struct DONAU_CharityPublicKeyP *charity_pub,
+ const struct DONAU_CharitySignatureP *charity_sig)
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
+ .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO),
+ .size = htonl (sizeof (purpose))
+ };
+
+ return GNUNET_CRYPTO_eddsa_verify_ (
+ DONAU_SIGNATURE_CHARITY_GET_INFO,
+ &purpose,
+ &charity_sig->eddsa_sig,
+ &charity_pub->eddsa_pub);
+}
+
+
+void
+DONAU_charity_get_info_sign (
+ const struct DONAU_CharityPrivateKeyP *charity_priv,
+ struct DONAU_CharitySignatureP *charity_sig)
+{
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
+ .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO),
+ .size = htonl (sizeof (purpose))
+ };
+
+ GNUNET_CRYPTO_eddsa_sign_ (&charity_priv->eddsa_priv,
+ &purpose,
+ &charity_sig->eddsa_sig);
+}
+
+
/* end of charity_signatures.c */