commit ce1b718f85191548b732929d19a6c36a56dcb7c1
parent 3f892c826b141826238559e3e0423f8a83da0ecc
Author: Bohdan Potuzhnyi <potub1@bfh.ch>
Date: Thu, 1 Aug 2024 14:49:34 +0200
db part is done(new approach)
Diffstat:
8 files changed, 432 insertions(+), 13 deletions(-)
diff --git a/src/challengerdb/Makefile.am b/src/challengerdb/Makefile.am
@@ -81,9 +81,11 @@ libchallenger_plugin_db_postgres_la_SOURCES = \
pg_token_add_token.h pg_token_add_token.c \
pg_setup_nonce.h pg_setup_nonce.c \
pg_authorize_start.h pg_authorize_start.c \
+ pg_authorize_start_pkce.h pg_authorize_start_pkce.c \
pg_challenge_set_address_and_pin.h pg_challenge_set_address_and_pin.c \
pg_validate_solve_pin.h pg_validate_solve_pin.c \
pg_validation_get.h pg_validation_get.c \
+ pg_validation_get_pkce.h pg_validation_get_pkce.c \
plugin_challengerdb_postgres.c pg_helper.h
libchallenger_plugin_db_postgres_la_LIBADD = \
$(LTLIBINTL)
diff --git a/src/challengerdb/challenger-0001.sql b/src/challengerdb/challenger-0001.sql
@@ -60,6 +60,11 @@ CREATE TABLE IF NOT EXISTS validations
,client_state VARCHAR
,client_redirect_uri VARCHAR
);
+
+ -- Add columns for PKCE (RFC 7636)
+ALTER TABLE validations
+ADD COLUMN IF NOT EXISTS code_challenge VARCHAR,
+ADD COLUMN IF NOT EXISTS code_challenge_method VARCHAR;
COMMENT ON TABLE validations
IS 'Active validations where we send a challenge to an address of a user';
@@ -87,6 +92,10 @@ COMMENT ON COLUMN validations.last_tx_time
IS 'When did we last sent the challenge (guard against DDoS)';
COMMENT ON COLUMN validations.expiration_time
IS 'When will the challenge expire';
+COMMENT ON COLUMN validations.code_challenge
+ IS 'Code challenge used for PKCE';
+COMMENT ON COLUMN validations.code_challenge_method
+ IS 'Code challenge method used for PKCE (plain, s256)';
CREATE INDEX IF NOT EXISTS validations_serial
ON validations (validation_serial_id);
diff --git a/src/challengerdb/pg_authorize_start_pkce.c b/src/challengerdb/pg_authorize_start_pkce.c
@@ -0,0 +1,106 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2023 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_authorize_start_pkce.c
+ * @brief Implementation of the authorize_start_pkce function for Postgres
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_authorize_start_pkce.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+CH_PG_authorize_start_pkce (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ uint64_t client_id,
+ const char *client_scope,
+ const char *client_state,
+ const char *client_redirect_uri,
+ const char *code_challenge,
+ const char *code_challenge_method,
+ json_t **last_address,
+ uint32_t *address_attempts_left,
+ uint32_t *pin_transmissions_left,
+ uint32_t *auth_attempts_left,
+ bool *solved,
+ struct GNUNET_TIME_Absolute *last_tx_time)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (nonce),
+ GNUNET_PQ_query_param_uint64 (&client_id),
+ NULL != client_scope
+ ? GNUNET_PQ_query_param_string (client_scope)
+ : GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_string (client_state),
+ NULL != client_redirect_uri
+ ? GNUNET_PQ_query_param_string (client_redirect_uri)
+ : GNUNET_PQ_query_param_null (),
+ NULL != code_challenge
+ ? GNUNET_PQ_query_param_string (code_challenge)
+ : GNUNET_PQ_query_param_null (),
+ NULL != code_challenge_method
+ ? GNUNET_PQ_query_param_string (code_challenge_method)
+ : GNUNET_PQ_query_param_null (),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("address",
+ last_address),
+ NULL),
+ GNUNET_PQ_result_spec_uint32 ("address_attempts_left",
+ address_attempts_left),
+ GNUNET_PQ_result_spec_uint32 ("pin_transmissions_left",
+ pin_transmissions_left),
+ GNUNET_PQ_result_spec_uint32 ("auth_attempts_left",
+ auth_attempts_left),
+ GNUNET_PQ_result_spec_bool ("solved",
+ solved),
+ GNUNET_PQ_result_spec_absolute_time ("last_tx_time",
+ last_tx_time),
+ GNUNET_PQ_result_spec_end
+ };
+
+ *last_address = NULL;
+ PREPARE (pg,
+ "authorize_start_validation",
+ "UPDATE validations SET"
+ " client_scope=$3"
+ " ,client_state=$4"
+ " ,client_redirect_uri=$5::VARCHAR"
+ " ,code_challenge=$6"
+ " ,code_challenge_method=$7"
+ " WHERE nonce=$1"
+ " AND client_serial_id=$2"
+ " AND ($5::VARCHAR=COALESCE(client_redirect_uri,$5::VARCHAR))"
+ " RETURNING"
+ " address"
+ " ,address_attempts_left"
+ " ,pin_transmissions_left"
+ " ,GREATEST(0, auth_attempts_left) AS auth_attempts_left"
+ " ,auth_attempts_left = -1 AS solved"
+ " ,last_tx_time;");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "authorize_start_validation",
+ params,
+ rs);
+}
diff --git a/src/challengerdb/pg_authorize_start_pkce.h b/src/challengerdb/pg_authorize_start_pkce.h
@@ -0,0 +1,72 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2023 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_authorize_start_pkce.h
+ * @brief implementation of the authorize_start_pkce function for Postgres
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#ifndef PG_LOGIN_START_PKCE_H
+#define PG_LOGIN_START_PKCE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "challenger_database_plugin.h"
+
+
+/**
+ * Set the user-provided address in a validation process. Updates
+ * the address and decrements the "addresses left" counter. If the
+ * address did not change, the operation is successful even without
+ * the counter change.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param client_id client that initiated the validation
+ * @param client_scope scope of the validation
+ * @param client_state state of the client
+ * @param client_redirect_uri where to redirect at the end, NULL to use a unique one registered for the client
+ * @param code_challenge PKCE code challenge
+ * @param code_challenge_method PKCE code challenge method
+ * @param[out] last_address set to the last address used
+ * @param[out] address_attempts_left set to number of address changing attempts left for this address
+ * @param[out] pin_transmissions_left set to number of times the PIN can still be re-requested
+ * @param[out] auth_attempts_left set to number of authentication attempts remaining
+ * @param[out] solved set to true if the challenge is already solved
+ * @param[out] last_tx_time set to the last time when we (presumably) send a PIN to @a last_address; 0 if never sent
+ * @return transaction status:
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the address was changed
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we do not permit further changes to the address (attempts exhausted)
+ * #GNUNET_DB_STATUS_HARD_ERROR on failure
+ */
+enum GNUNET_DB_QueryStatus
+CH_PG_authorize_start_pkce (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ uint64_t client_id,
+ const char *client_scope,
+ const char *client_state,
+ const char *client_redirect_uri,
+ const char *code_challenge,
+ const char *code_challenge_method,
+ json_t **last_address,
+ uint32_t *address_attempts_left,
+ uint32_t *pin_transmissions_left,
+ uint32_t *auth_attempts_left,
+ bool *solved,
+ struct GNUNET_TIME_Absolute *last_tx_time);
+
+
+#endif
diff --git a/src/challengerdb/pg_validation_get_pkce.c b/src/challengerdb/pg_validation_get_pkce.c
@@ -0,0 +1,94 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2023 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_validation_get_pkce.c
+ * @brief Implementation of the validation_get_pkce function for Postgres
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_validation_get_pkce.h"
+#include "pg_helper.h"
+
+enum GNUNET_DB_QueryStatus
+CH_PG_validation_get_pkce (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ char **client_secret,
+ json_t **address,
+ char **client_scope,
+ char **client_state,
+ char **client_redirect_uri,
+ char **code_challenge,
+ char **code_challenge_method)
+{
+ 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_string ("client_secret",
+ client_secret),
+ GNUNET_PQ_result_spec_allow_null (
+ TALER_PQ_result_spec_json ("address",
+ address),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("client_scope",
+ client_scope),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("client_state",
+ client_state),
+ NULL),
+ GNUNET_PQ_result_spec_string ("redirect_uri",
+ client_redirect_uri),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("code_challenge",
+ code_challenge),
+ NULL),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("code_challenge_method",
+ code_challenge_method),
+ NULL),
+ GNUNET_PQ_result_spec_end
+ };
+
+ *client_scope = NULL;
+ *client_state = NULL;
+ *address = NULL;
+ PREPARE (pg,
+ "validation_get",
+ "SELECT "
+ " client_secret"
+ " ,address"
+ " ,client_scope"
+ " ,client_state"
+ " ,COALESCE(client_redirect_uri,uri) AS redirect_uri"
+ " ,code_challenge"
+ " ,code_challenge_method"
+ " FROM validations"
+ " JOIN clients "
+ " USING (client_serial_id)"
+ " WHERE nonce=$1");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "validation_get",
+ params,
+ rs);
+}
diff --git a/src/challengerdb/pg_validation_get_pkce.h b/src/challengerdb/pg_validation_get_pkce.h
@@ -0,0 +1,60 @@
+/*
+ This file is part of Challenger
+ Copyright (C) 2023 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_validation_get_pkce.h
+ * @brief implementation of the validation_get_pkce function for Postgres
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#ifndef PG_VALIDATION_GET_PKCE_H
+#define PG_VALIDATION_GET_PKCE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "challenger_database_plugin.h"
+
+
+/**
+ * Return validation details. Used by ``/solve``, ``/auth`` and
+ * ``/info`` endpoints to authorize and return validated user
+ * address to the client.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param[out] client_secret set to secret of client (for client that setup the challenge)
+ * @param[out] address set to client-provided address
+ * @param[out] client_scope set to OAuth2 scope
+ * @param[out] client_state set to client state
+ * @param[out] client_redirect_uri set to client redirect URL
+ * @param[out] code_challenge set to PKCE code challenge
+ * @param[out] code_challenge_method set to PKCE code challenge method
+ * @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_validation_get_pkce (void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ char **client_secret,
+ json_t **address,
+ char **client_scope,
+ char **client_state,
+ char **client_redirect_uri,
+ char **code_challenge,
+ char **code_challenge_method);
+
+#endif
diff --git a/src/challengerdb/plugin_challengerdb_postgres.c b/src/challengerdb/plugin_challengerdb_postgres.c
@@ -34,9 +34,11 @@
#include "pg_client_check.h"
#include "pg_setup_nonce.h"
#include "pg_authorize_start.h"
+#include "pg_authorize_start_pkce.h"
#include "pg_challenge_set_address_and_pin.h"
#include "pg_validate_solve_pin.h"
#include "pg_validation_get.h"
+#include "pg_validation_get_pkce.h"
/**
* Drop challenger tables
@@ -405,16 +407,20 @@ libchallenger_plugin_db_postgres_init (void *cls)
= &CH_PG_setup_nonce;
plugin->authorize_start
= &CH_PG_authorize_start;
+ plugin->authorize_start_pkce
+ = &CH_PG_authorize_start_pkce;
plugin->challenge_set_address_and_pin
= &CH_PG_challenge_set_address_and_pin;
plugin->validate_solve_pin
= &CH_PG_validate_solve_pin;
plugin->validation_get
= &CH_PG_validation_get;
+ plugin->validation_get_pkce
+ = &CH_PG_validation_get_pkce;
plugin->info_get_token
= &CH_PG_info_get_token;
plugin->token_add_token
- = &CH_PG_token_add_token;
+ = &CH_PG_token_add_token;
return plugin;
}
diff --git a/src/include/challenger_database_plugin.h b/src/include/challenger_database_plugin.h
@@ -257,21 +257,62 @@ struct CHALLENGER_DatabasePlugin
* #GNUNET_DB_STATUS_HARD_ERROR on failure
*/
enum GNUNET_DB_QueryStatus
- (*authorize_start)(void *cls,
- const struct CHALLENGER_ValidationNonceP *nonce,
- uint64_t client_id,
- const char *client_scope,
- const char *client_state,
- const char *client_redirect_uri,
- json_t **last_address,
- uint32_t *address_attempts_left,
- uint32_t *pin_transmissions_left,
- uint32_t *auth_attempts_left,
- bool *solved,
- struct GNUNET_TIME_Absolute *last_tx_time);
+ (*authorize_start)(void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ uint64_t client_id,
+ const char *client_scope,
+ const char *client_state,
+ const char *client_redirect_uri,
+ json_t **last_address,
+ uint32_t *address_attempts_left,
+ uint32_t *pin_transmissions_left,
+ uint32_t *auth_attempts_left,
+ bool *solved,
+ struct GNUNET_TIME_Absolute *last_tx_time);
/**
+ * Set the user-provided address and PKCE parameters in a validation process.
+ * Updates the address and decrements the "addresses left" counter. If the
+ * address did not change, the operation is successful even without
+ * the counter change.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param client_id client that initiated the validation
+ * @param client_scope scope of the validation
+ * @param client_state state of the client
+ * @param client_redirect_uri where to redirect at the end, NULL to use a unique one registered for the client
+ * @param code_challenge PKCE code challenge
+ * @param code_challenge_method PKCE code challenge method
+ * @param[out] last_address set to the last address used
+ * @param[out] address_attempts_left set to number of address changing attempts left for this address
+ * @param[out] pin_transmissions_left set to number of times the PIN can still be re-requested
+ * @param[out] auth_attempts_left set to number of authentication attempts remaining
+ * @param[out] solved set to true if the challenge is already solved
+ * @param[out] last_tx_time set to the last time when we (presumably) send a PIN to @a last_address; 0 if never sent
+ * @return transaction status:
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the address was changed
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we do not permit further changes to the address (attempts exhausted)
+ * #GNUNET_DB_STATUS_HARD_ERROR on failure
+ */
+ enum GNUNET_DB_QueryStatus
+ (*authorize_start_pkce)(void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ uint64_t client_id,
+ const char *client_scope,
+ const char *client_state,
+ const char *client_redirect_uri,
+ const char *code_challenge,
+ const char *code_challenge_method,
+ json_t **last_address,
+ uint32_t *address_attempts_left,
+ uint32_t *pin_transmissions_left,
+ uint32_t *auth_attempts_left,
+ bool *solved,
+ struct GNUNET_TIME_Absolute *last_tx_time);
+
+ /**
* Set the user-provided address in a validation process. Updates
* the address and decrements the "addresses left" counter. If the
* address did not change, the operation is successful even without
@@ -369,6 +410,35 @@ struct CHALLENGER_DatabasePlugin
char **client_state,
char **client_redirect_uri);
+
+ /**
+ * Return validation details including PKCE parameters. Used by `/solve`, `/auth`, and
+ * `/info` endpoints to authorize and return validated user address to the client.
+ *
+ * @param cls
+ * @param nonce unique nonce to use to identify the validation
+ * @param[out] client_secret set to secret of client (for client that setup the challenge)
+ * @param[out] address set to client-provided address
+ * @param[out] client_scope set to OAuth2 scope
+ * @param[out] client_state set to client state
+ * @param[out] client_redirect_uri set to client redirect URL
+ * @param[out] code_challenge set to PKCE code challenge
+ * @param[out] code_challenge_method set to PKCE code challenge method
+ * @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
+ (*validation_get_pkce)(void *cls,
+ const struct CHALLENGER_ValidationNonceP *nonce,
+ char **client_secret,
+ json_t **address,
+ char **client_scope,
+ char **client_state,
+ char **client_redirect_uri,
+ char **code_challenge,
+ char **code_challenge_method);
/**
* Add access @a grant to address under @a nonce.