challenger

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

commit 713758141eeb9d0d26fafd8eb59a73167d98f706
parent b4664821d397aec393e2c3449d1657ec2b89a3c0
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 14 Feb 2024 20:54:57 +0100

preparations for #8405

Diffstat:
Msrc/challenger/challenger-httpd_solve.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/challengerdb/pg_validate_solve_pin.c | 68+++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/challengerdb/pg_validate_solve_pin.h | 17+++++++++++++----
Msrc/challengerdb/procedures.sql.in | 1+
Msrc/include/challenger_database_plugin.h | 10+++++++++-
5 files changed, 136 insertions(+), 52 deletions(-)

diff --git a/src/challenger/challenger-httpd_solve.c b/src/challenger/challenger-httpd_solve.c @@ -46,6 +46,11 @@ struct SolveContext struct MHD_PostProcessor *pp; /** + * OAuth2 client redirection URI for error reporting. + */ + char *client_redirect_uri; + + /** * 0-terminated PIN submitted to us. */ char *pin; @@ -54,6 +59,22 @@ struct SolveContext * Number of bytes in @a pin, excluding 0-terminator. */ size_t pin_len; + + /** + * How many address changes are still allowed? + */ + uint32_t addr_left; + + /** + * How many authentication attempts are still allowed? + */ + uint32_t auth_attempts_left; + + /** + * How many pin transmissions can still be requested? + */ + uint32_t pin_transmissions_left; + }; @@ -206,36 +227,71 @@ CH_handler_solve (struct CH_HandlerContext *hc, qs = CH_db->validate_solve_pin (CH_db->cls, &bc->nonce, pin, - &solved); + &solved, + &bc->addr_left, + &bc->auth_attempts_left, + &bc->pin_transmissions_left, + &bc->client_redirect_uri); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_TEMPLATING_reply_error (hc->connection, - "internal-error", - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "validate_solve_pin"); + return TALER_TEMPLATING_reply_error ( + hc->connection, + "internal-error", + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "validate_solve_pin"); case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); return MHD_NO; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_TEMPLATING_reply_error (hc->connection, - "validation-unknown", - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, - NULL); + return TALER_TEMPLATING_reply_error ( + hc->connection, + "validation-unknown", + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } if (! solved) { - // FIXME: if no more attempts remaining, - // redirect to server instead! - return TALER_TEMPLATING_reply_error (hc->connection, - "invalid-pin", - MHD_HTTP_FORBIDDEN, - TALER_EC_CHALLENGER_INVALID_PIN, - NULL); + MHD_RESULT ret; + json_t *details; + + if ( (0 == bc->addr_left) && + (0 == bc->pin_transmissions_left) && + (0 == bc->auth_attempts_left) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client exhausted all chances to satisfy challenge\n"); + return TALER_MHD_redirect_with_oauth_status ( + hc->connection, + bc->client_redirect_uri, + "access_denied", + "users exhausted all possibilities of passing the check", + NULL); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid PIN supplied, client has chance to solve it again\n"); + details = GNUNET_JSON_PACK ( + TALER_JSON_pack_ec (TALER_EC_CHALLENGER_INVALID_PIN), + GNUNET_JSON_pack_uint64 ("addresses_left", + bc->addr_left), + GNUNET_JSON_pack_uint64 ("pin_transmissions_left", + bc->pin_transmissions_left), + GNUNET_JSON_pack_uint64 ("auth_attempts_left", + bc->auth_attempts_left) + ); + ret = TALER_TEMPLATING_reply (hc->connection, + MHD_HTTP_FORBIDDEN, + "invalid-pin", + NULL, + NULL, + details); + json_decref (details); + return ret; } } diff --git a/src/challengerdb/pg_validate_solve_pin.c b/src/challengerdb/pg_validate_solve_pin.c @@ -30,7 +30,11 @@ enum GNUNET_DB_QueryStatus CH_PG_validate_solve_pin (void *cls, const struct CHALLENGER_ValidationNonceP *nonce, uint32_t new_pin, - bool *solved) + bool *solved, + uint32_t *addr_left, + uint32_t *auth_attempts_left, + uint32_t *pin_transmissions_left, + char **client_redirect_uri) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -38,39 +42,45 @@ CH_PG_validate_solve_pin (void *cls, GNUNET_PQ_query_param_uint32 (&new_pin), GNUNET_PQ_query_param_end }; + bool not_found; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("not_found", + &not_found), GNUNET_PQ_result_spec_bool ("solved", solved), + GNUNET_PQ_result_spec_uint32 ("address_attempts_left", + addr_left), + GNUNET_PQ_result_spec_uint32 ("auth_attempts_left", + auth_attempts_left), + GNUNET_PQ_result_spec_uint32 ("pin_transmissions_left", + pin_transmissions_left), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("client_redirect_uri", + client_redirect_uri), + NULL), GNUNET_PQ_result_spec_end }; + enum GNUNET_DB_QueryStatus qs; - /* We set all remaining attempts to zero to prevent - user from changing the address after already having - succeeded with the process. */ + *client_redirect_uri = NULL; PREPARE (pg, - "validate_solve_pin", - "UPDATE validations SET" - " auth_attempts_left=CASE" - " WHEN last_pin = $2" - " THEN 0" - " ELSE auth_attempts_left - 1" - " END" - " ,address_attempts_left=CASE" - " WHEN last_pin = $2" - " THEN 0" - " ELSE address_attempts_left" - " END" - " ,pin_transmissions_left=CASE" - " WHEN last_pin = $2" - " THEN 0" - " ELSE pin_transmissions_left" - " END" - " WHERE nonce=$1" - " AND auth_attempts_left > 0" - " RETURNING" - " (last_pin = $2) AS solved;"); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "validate_solve_pin", - params, - rs); + "do_validate_solve_pin", + "SELECT " + " out_not_found AS not_found" + ",out_solved AS solved" + ",out_address_attempts_left AS address_attempts_left" + ",out_auth_attempts_left AS auth_attempts_left" + ",out_pin_transmissions_left AS pin_transmissions_left" + ",out_client_redirect_uri AS client_redirect_uri" + " FROM challenger_do_validate_solve_pin" + " ($1,$2);"); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "do_validate_solve_pin", + params, + rs); + if (qs <= 0) + return qs; + if (not_found) + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + return qs; } diff --git a/src/challengerdb/pg_validate_solve_pin.h b/src/challengerdb/pg_validate_solve_pin.h @@ -33,16 +33,25 @@ * @param nonce unique nonce to use to identify the validation * @param new_pin the PIN the user entered * @param[out] solved set to true if the PIN was correct + * @param[out] addr_left set to number of address changes remaining + * @param[out] auth_attempts_left set to number of authentication attempts remaining + * @param[out] pin_transmissions_left set to number of times the PIN can still be re-requested + * @param[out] client_redirect_uri set to OAuth2 client redirect URI * @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_validate_solve_pin (void *cls, - const struct CHALLENGER_ValidationNonceP *nonce, - uint32_t new_pin, - bool *solved); +CH_PG_validate_solve_pin ( + void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + uint32_t new_pin, + bool *solved, + uint32_t *addr_left, + uint32_t *auth_attempts_left, + uint32_t *pin_transmissions_left, + char **client_redirect_uri); #endif diff --git a/src/challengerdb/procedures.sql.in b/src/challengerdb/procedures.sql.in @@ -19,5 +19,6 @@ BEGIN; SET search_path TO challenger; #include "challenger_do_challenge_set_address_and_pin.sql" +#include "challenger_do_validate_and_solve_pin.sql" COMMIT; diff --git a/src/include/challenger_database_plugin.h b/src/include/challenger_database_plugin.h @@ -289,6 +289,10 @@ struct CHALLENGER_DatabasePlugin * @param nonce unique nonce to use to identify the validation * @param new_pin the PIN the user entered * @param[out] solved set to true if the PIN was correct + * @param[out] addr_left set to number of address changes remaining + * @param[out] auth_attempts_left set to number of authentication attempts remaining + * @param[out] pin_transmissions_left set to number of times the PIN can still be re-requested + * @param[out] client_redirect_uri set to OAuth2 client redirect URI * @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 @@ -298,7 +302,11 @@ struct CHALLENGER_DatabasePlugin (*validate_solve_pin)(void *cls, const struct CHALLENGER_ValidationNonceP *nonce, uint32_t new_pin, - bool *solved); + bool *solved, + uint32_t *addr_left, + uint32_t *auth_attempts_left, + uint32_t *pin_transmissions_left, + char **client_redirect_uri); /**