commit 733e4c43afdf1cb2d46b1c05f0b43aef10bd03cb
parent dd72e4d0b0d108c7d90e1f22f1d2e2ef0c0271fe
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Thu, 17 Apr 2025 11:30:48 +0200
fix #9349
Diffstat:
10 files changed, 245 insertions(+), 9 deletions(-)
diff --git a/src/challenger/challenger-httpd.c b/src/challenger/challenger-httpd.c
@@ -445,9 +445,10 @@ handle_mhd_completion_callback (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Finished handling request with status %d (HTTP status %u)\n",
(int) toe,
- ci->http_status);
+ (NULL != ci) ? ci->http_status : 0);
if (NULL != hc->cc)
hc->cc (hc->ctx);
+ GNUNET_free (hc->full_url);
GNUNET_free (hc);
*con_cls = NULL;
}
diff --git a/src/challenger/challenger-httpd_challenge.c b/src/challenger/challenger-httpd_challenge.c
@@ -34,6 +34,11 @@
#define MAX_RETRIES 3
/**
+ * Set to 1 to dump addresses into the log.
+ */
+#define DEBUG 0
+
+/**
* Context for a /challenge operation.
*/
struct ChallengeContext
@@ -100,6 +105,11 @@ struct ChallengeContext
char *state;
/**
+ * Buffer used by #TALER_MHD_parse_post_json(). Or NULL.
+ */
+ void *jbuffer;
+
+ /**
* When did we transmit last?
*/
struct GNUNET_TIME_Absolute last_tx_time;
@@ -164,6 +174,11 @@ struct ChallengeContext
* Did we do the DB interaction?
*/
bool db_finished;
+
+ /**
+ * Is the upload in JSON?
+ */
+ bool is_json;
};
@@ -222,6 +237,7 @@ cleanup_ctx (void *cls)
GNUNET_OS_process_wait (bc->child));
bc->child = NULL;
}
+ TALER_MHD_parse_post_cleanup_callback (bc->jbuffer);
json_decref (bc->address);
GNUNET_free (bc->data);
GNUNET_free (bc->state);
@@ -321,10 +337,12 @@ send_tan (struct ChallengeContext *bc)
address = json_dumps (bc->address,
JSON_COMPACT);
+#if DEBUG
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Running auth command `%s' on address `%s'\n",
CH_auth_command,
address);
+#endif
bc->child = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
p,
NULL,
@@ -579,7 +597,6 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
hc->cc = &cleanup_ctx;
hc->ctx = bc;
bc->pst = GNUNET_OS_PROCESS_UNKNOWN;
- bc->address = json_object ();
bc->tan
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
100000000);
@@ -600,6 +617,21 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
TALER_EC_GENERIC_PARAMETER_MISSING,
hc->path);
}
+ {
+ const char *ct;
+
+ ct = MHD_lookup_connection_value (hc->connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONTENT_TYPE);
+ bc->is_json = ( (NULL != ct) &&
+ (0 == strcasecmp (ct,
+ "application/json")) );
+ if (! bc->is_json)
+ bc->address = json_object ();
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Processing /challenge upload with %s encoding...\n",
+ ct);
+ }
TALER_MHD_check_content_length (hc->connection,
1024);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -633,18 +665,38 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
es);
}
/* handle upload */
- if (0 != *upload_data_size)
+ if (bc->is_json)
+ {
+ if (NULL == bc->address)
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_post_json (hc->connection,
+ &bc->jbuffer,
+ upload_data,
+ upload_data_size,
+ &bc->address);
+ if (GNUNET_SYSERR != res)
+ return MHD_YES;
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ else
+ {
+ GNUNET_break (0 == *upload_data_size);
+ }
+ }
+ else if (0 != *upload_data_size)
{
enum MHD_Result res;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Processing /challenge upload...\n");
res = MHD_post_process (bc->pp,
upload_data,
*upload_data_size);
*upload_data_size = 0;
if (MHD_YES == res)
return MHD_YES;
+ GNUNET_break (0);
return MHD_NO;
}
if (NULL != bc->last_key)
@@ -657,6 +709,7 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
bc->data_len = 0;
GNUNET_free (bc->last_key);
}
+#if DEBUG
{
char *address;
@@ -667,6 +720,7 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
address);
free (address);
}
+#endif
{
const char *bad_field;
@@ -686,8 +740,59 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
for (unsigned int r = 0; r < MAX_RETRIES; r++)
{
enum GNUNET_DB_QueryStatus qs;
+ json_t *old_address;
+ const json_t *ro;
GNUNET_assert (NULL == bc->client_redirect_uri);
+ qs = CH_db->address_get (
+ CH_db->cls,
+ &bc->nonce,
+ &old_address);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return reply_error (bc,
+ "internal-error",
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "validation-get");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ if (r < MAX_RETRIES - 1)
+ continue;
+ GNUNET_break (0);
+ return reply_error (bc,
+ "internal-error",
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "validation-get");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break_op (0);
+ return reply_error (bc,
+ "validation-unknown",
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN,
+ NULL);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ ro = json_object_get (old_address,
+ "read_only");
+ if ( (NULL != ro) &&
+ (json_boolean_value (ro)) &&
+ (1 != json_equal (old_address,
+ bc->address)) )
+ {
+ GNUNET_break_op (0);
+ json_decref (old_address);
+ return reply_error (bc,
+ "address-read-only",
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_READ_ONLY,
+ NULL);
+ }
+ json_decref (old_address);
+
qs = CH_db->challenge_set_address_and_pin (
CH_db->cls,
&bc->nonce,
diff --git a/src/challenger/challenger-httpd_setup.c b/src/challenger/challenger-httpd_setup.c
@@ -29,6 +29,11 @@
*/
#define MAX_RETRIES 3
+/**
+ * Set to 1 to dump addresses into the log.
+ */
+#define DEBUG 0
+
struct SetupContext
{
@@ -122,9 +127,11 @@ CH_handler_setup (struct CH_HandlerContext *hc,
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Address data uploaded in /setup\n");
+#if DEBUG
json_dumpf (sc->root,
stderr,
JSON_INDENT (2));
+#endif
}
{
diff --git a/src/challenger/challenger.conf b/src/challenger/challenger.conf
@@ -32,6 +32,7 @@ VALIDATION_EXPIRATION = 365d
# Base URL of our service. Must end with '/'.
#BASE_URL = https://challenger.DOMAIN/
+BASE_URL = http://localhost:9967/
# Name of a file with the message to send with the challenge.
MESSAGE_TEMPLATE_FILE = ${DATADIR}templates/default-challenge-message.txt
@@ -39,11 +40,12 @@ MESSAGE_TEMPLATE_FILE = ${DATADIR}templates/default-challenge-message.txt
# Which external command should be used to transmit challenges?
# Example commands are challenger-send-{sms,email,post}.sh
# AUTH_COMMAND =
+AUTH_COMMAND = /usr/bin/true
-
-# What address type are we validating? (phone, email, address, etc.)
+# What address type are we validating? (phone, email, postal, etc.)
# A template of the form 'enter-$ADDRESS_TYPE-form' must
# exist and the field names must be supported by the
# AUTH_COMMAND.
#
# ADDRESS_TYPE =
+ADDRESS_TYPE = postal
diff --git a/src/challengerdb/Makefile.am b/src/challengerdb/Makefile.am
@@ -71,6 +71,7 @@ libchallengerdb_la_LDFLAGS = \
-no-undefined
libchallenger_plugin_db_postgres_la_SOURCES = \
+ pg_address_get.h pg_address_get.c \
pg_client_add.h pg_client_add.c \
pg_client_modify.h pg_client_modify.c \
pg_client_delete.h pg_client_delete.c \
diff --git a/src/challengerdb/challenger-0001.sql b/src/challengerdb/challenger-0001.sql
@@ -61,8 +61,6 @@ CREATE TABLE IF NOT EXISTS validations
,client_redirect_uri VARCHAR
);
-
-
COMMENT ON TABLE validations
IS 'Active validations where we send a challenge to an address of a user';
COMMENT ON COLUMN validations.client_serial_id
diff --git a/src/challengerdb/pg_address_get.c b/src/challengerdb/pg_address_get.c
@@ -0,0 +1,57 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2025 Taler Systems SA
+
+ Challenger 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 3, or (at your option) any later version.
+
+ Challenger 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
+ Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file challengerdb/pg_address_get.c
+ * @brief Implementation of the address_get function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_address_get.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+CH_PG_address_get (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ json_t **address)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (nonce),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("address",
+ address),
+ NULL),
+ GNUNET_PQ_result_spec_end
+ };
+
+ *address = NULL;
+ PREPARE (pg,
+ "address_get",
+ "SELECT "
+ " address"
+ " FROM validations"
+ " WHERE nonce=$1");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "address_get",
+ params,
+ rs);
+}
diff --git a/src/challengerdb/pg_address_get.h b/src/challengerdb/pg_address_get.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2025 Taler Systems SA
+
+ Challenger 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 3, or (at your option) any later version.
+
+ Challenger 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
+ Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file challengerdb/pg_address_get.h
+ * @brief implementation of the address_get function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_ADDRESS_GET_H
+#define PG_ADDRESS_GET_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "challenger_database_plugin.h"
+
+
+/**
+ * Return address details.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param[out] address set to client-provided address (or to NULL)
+ * @return transaction status:
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the nonce was found
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we do not know the nonce
+ * #GNUNET_DB_STATUS_HARD_ERROR on failure
+ */
+enum GNUNET_DB_QueryStatus
+CH_PG_address_get (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ json_t **address);
+
+#endif
diff --git a/src/challengerdb/plugin_challengerdb_postgres.c b/src/challengerdb/plugin_challengerdb_postgres.c
@@ -26,6 +26,7 @@
#include "challenger_database_plugin.h"
#include "challenger_database_lib.h"
#include "pg_helper.h"
+#include "pg_address_get.h"
#include "pg_client_add.h"
#include "pg_client_modify.h"
#include "pg_client_delete.h"
@@ -388,6 +389,8 @@ libchallenger_plugin_db_postgres_init (void *cls)
= &postgres_commit_transaction;
plugin->rollback
= &postgres_rollback;
+ plugin->address_get
+ = &CH_PG_address_get;
plugin->client_add
= &CH_PG_client_add;
plugin->client_modify
diff --git a/src/include/challenger_database_plugin.h b/src/include/challenger_database_plugin.h
@@ -393,6 +393,23 @@ struct CHALLENGER_DatabasePlugin
/**
+ * Return address known for a particular nonce.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param[out] address set to client-provided address, can be set to NULL!
+ * @return transaction status:
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the nonce was found
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we do not know the nonce
+ * #GNUNET_DB_STATUS_HARD_ERROR on failure
+ */
+ enum GNUNET_DB_QueryStatus
+ (*address_get)(void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ json_t **address);
+
+
+ /**
* Return validation details including PKCE parameters. Used by `/solve`, `/auth`, and
* `/info` endpoints to authorize and return validated user address to the client.
*