challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/challengerdb/Makefile.am | 2++
Msrc/challengerdb/challenger-0001.sql | 9+++++++++
Asrc/challengerdb/pg_authorize_start_pkce.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challengerdb/pg_authorize_start_pkce.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challengerdb/pg_validation_get_pkce.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challengerdb/pg_validation_get_pkce.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/challengerdb/plugin_challengerdb_postgres.c | 8+++++++-
Msrc/include/challenger_database_plugin.h | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
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.