challenger

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

commit 7d6115ab6aca23d906b2550e6a87f8d4a248fbad
parent 450ce1ddef69afdb25050aa389d488884dee6b98
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 25 Apr 2023 12:45:07 +0200

work on DB API design

Diffstat:
Msrc/challenger/challenger-httpd.c | 13++++++++++++-
Asrc/challenger/challenger-httpd_setup.c | 36++++++++++++++++++++++++++++++++++++
Asrc/challenger/challenger-httpd_setup.h | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/challengerdb/challenger-0001.sql | 21+++++++++++++++------
Msrc/include/challenger_database_plugin.h | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 273 insertions(+), 7 deletions(-)

diff --git a/src/challenger/challenger-httpd.c b/src/challenger/challenger-httpd.c @@ -27,6 +27,7 @@ #include "challenger-httpd_auth.h" #include "challenger-httpd_challenge.h" #include "challenger-httpd_info.h" +#include "challenger-httpd_setup.h" #include "challenger-httpd_login.h" #include "challenger-httpd_mhd.h" #include "challenger-httpd_solve.h" @@ -142,8 +143,18 @@ url_handler (void *cls, .handler = &CH_handler_config }, { - .url = "/login", + .url = "/setup", .method = MHD_HTTP_METHOD_GET, + .handler = &CH_handler_setup + }, + { + .url = "/setup", + .method = MHD_HTTP_METHOD_POST, + .handler = &CH_handler_setup + }, + { + .url = "/login", + .method = MHD_HTTP_METHOD_POST, .handler = &CH_handler_login }, { diff --git a/src/challenger/challenger-httpd_setup.c b/src/challenger/challenger-httpd_setup.c @@ -0,0 +1,36 @@ +/* + 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file challenger-httpd_setup.c + * @brief functions to handle incoming requests for setups + * @author Christian Grothoff + */ +#include "platform.h" +#include "challenger-httpd.h" +#include <gnunet/gnunet_util_lib.h> +#include "challenger-httpd_setup.h" + + +MHD_RESULT +CH_handler_setup (struct CH_HandlerContext *hc, + const char *upload_data, + size_t *upload_data_size) +{ + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + NULL); +} diff --git a/src/challenger/challenger-httpd_setup.h b/src/challenger/challenger-httpd_setup.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file challenger-httpd_login.h + * @brief functions to handle incoming requests on /login + * @author Christian Grothoff + */ +#ifndef CHALLENGER_HTTPD_SETUP_H +#define CHALLENGER_HTTPD_SETUP_H + +#include <microhttpd.h> + + +/** + * Handle request on @a connection for /setup. + * + * @param hc context of the connection + * @param upload_data upload data, if any + * @param[in,out] upload_data_size remaining data in @a upload_data, to be updated + * @return MHD result code + */ +MHD_RESULT +CH_handler_setup (struct CH_HandlerContext *hc, + const char *upload_data, + size_t *upload_data_size); + + +#endif diff --git a/src/challengerdb/challenger-0001.sql b/src/challengerdb/challenger-0001.sql @@ -46,20 +46,25 @@ CREATE INDEX IF NOT EXISTS clients_serial CREATE TABLE IF NOT EXISTS validations (validation_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY ,client_serial_id INT8 NOT NULL REFERENCES clients (client_serial_id) - ,last_pin INT4 NOT NULL - ,attempts_left INT4 DEFAULT(3) - ,last_tx_time INT8 NOT NULL + ,nonce BYTEA CHECK (length(nonce)=32) UNIQUE ,expiration_time INT8 NOT NULL + ,last_tx_time INT8 NOT NULL DEFAULT (0) + ,address_attempts_left INT4 DEFAULT(3) + ,last_pin INT4 NOT NULL + ,pin_attempts_left INT4 DEFAULT(0) + ,auth_attempts_left INT4 DEFAULT(0) ,address VARCHAR ,client_scope VARCHAR NOT NULL ,client_state VARCHAR NOT NULL - ,client_redirect_uri VARCHAR NOT NULL + ,client_redirect_url VARCHAR NOT NULL ); 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 IS 'Which client initiated this validation'; +COMMENT ON COLUMN validations.nonce + IS 'Unguessable validation identifier'; COMMENT ON COLUMN validations.client_scope IS 'Client-specific scope value identifying the requested scope'; COMMENT ON COLUMN validations.client_state @@ -70,8 +75,12 @@ COMMENT ON COLUMN validations.address IS 'Address we are validating; provided by the user-agent; usually a phone number or e-mail address (depends on the client_scope)'; COMMENT ON COLUMN validations.last_pin IS 'Last PIN code send to the user'; -COMMENT ON COLUMN validations.attempts_left - IS 'How many more attempts do we permit (guard against brute-forcing)'; +COMMENT ON COLUMN validations.address_attempts_left + IS 'How many more address changes is the user allowed to make (guard against DoS and brute-forcing)'; +COMMENT ON COLUMN validations.pin_attempts_left + IS 'How many more PIN transmission attempts do we permit (guard against DoS and brute-forcing)'; +COMMENT ON COLUMN validations.auth_attempts_left + IS 'How many more authentication attempts do we permit (guard against brute-forcing)'; COMMENT ON COLUMN validations.last_tx_time IS 'When did we last sent the challenge (guard against DDoS)'; COMMENT ON COLUMN validations.expiration_time diff --git a/src/include/challenger_database_plugin.h b/src/include/challenger_database_plugin.h @@ -28,6 +28,18 @@ /** + * Nonce used to uniquely (and unpredictably) identify validations. + */ +struct CHALLENGER_ValidationNonceP +{ + /** + * 256-bit nonce used to identify validations. + */ + uint32_t [256 / 32]; +}; + + +/** * Handle to interact with the database. * * Functions ending with "_TR" run their OWN transaction scope @@ -154,5 +166,162 @@ struct CHALLENGER_DatabasePlugin (*client_delete)(void *cls, const char *client_url); + + /** + * Check if a client is in the list of authorized clients. If @a + * counter_increment is non-zero, the validation counter of the + * client is incremented by the given value if the client was found. + * + * @param cls + * @param client_url URL of the client + * @param client_secret secret of the client + * @param counter_increment change in validation counter + * @param[out] client_id set to unique row of the client + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*client_check)(void *cls, + const char *client_url, + const char *client_secret, + uint32_t counter_increment, + uint64_t *client_id); + + + /** + * Start validation process by setting up a validation entry. Allows + * the respective user to later begin the process. + * + * @param cls + * @param client_id ID of the client + * @param nonce unique nonce to use to identify the validation + * @param expiration_time when will the validation expire + * @param client_scope scope of the validation + * @param client_state state of the client + * @param client_redirect_url where to redirect at the end + * @return transaction status + */ + enum GNUNET_DB_QueryStatus + (*validation_setup)(void *cls, + uint64_t client_id, + const struct CHALLENGER_ValidationNonceP *nonce, + struct GNUNET_TIME_Absolute expiration_time, + const char *client_scope, + const char *client_state, + const char *client_redirect_url); + + + /** + * 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 address the new address to validate + * @param[out] last_tx_time set to the last time when we (presumably) send a PIN to @a address; 0 if never sent + * @param[out] last_pin set to the PIN last send to @a address, 0 if never sent + * @param[in,out] pin_attempts_left set to number of PIN transmission attempts left for this address; input is value to be used if address is new, output is possibly different if address was not new + * @return transaction status: + * #GNUNET_DB_SUCCESS_ONE_RESULT if the address was changed + * #GNUNET_DB_SUCCESS_NO_RESULTS if we do not permit further changes to the address (attempts exhausted) + * #GNUNET_DB_SUCCESS_HARD_ERROR on failure + */ + enum GNUNET_DB_QueryStatus + (*validate_login_address)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + const char *address, + struct GNUNET_TIME_Absolute *last_tx_time, + uint32_t *last_pin, + uint32_t *pin_attempts_left); + + + /** + * Store a new PIN to be used to validate an + * address. + * + * @param cls + * @param nonce unique nonce to use to identify the validation + * @param tx_time the current time + * @param new_pin the PIN we are sending + * @param auth_attempts_allowed how many attempts do we give to the user to enter the correct PIN + * @return transaction status: + * #GNUNET_DB_SUCCESS_ONE_RESULT if the pin was stored + * #GNUNET_DB_SUCCESS_NO_RESULTS if we do not know the @a nonce or if pin attempts left is zero + * #GNUNET_DB_SUCCESS_HARD_ERROR on failure + */ + enum GNUNET_DB_QueryStatus + (*validate_login_pin)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + struct GNUNET_TIME_Absolute tx_time, + uint32_t new_pin, + uint32_t auth_attempts_allowed); + + + /** + * Check if challenge is pending to validate an address. + * + * @param cls + * @param nonce unique nonce to use to identify the validation + * @param[out] open set to true if a challenge was sent + * @return transaction status: + * #GNUNET_DB_SUCCESS_ONE_RESULT if the nonce was found + * #GNUNET_DB_SUCCESS_NO_RESULTS if we do not know the nonce + * #GNUNET_DB_SUCCESS_HARD_ERROR on failure + */ + enum GNUNET_DB_QueryStatus + (*validate_challenge_open)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + bool *open); + + + /** + * Check PIN entered to validate an address. + * + * @param cls + * @param nonce unique nonce to use to identify the validation + * @param pin the PIN the user entered + * @param[out] solved set to true if the PIN was correct + * @return transaction status: + * #GNUNET_DB_SUCCESS_ONE_RESULT if the nonce was found + * #GNUNET_DB_SUCCESS_NO_RESULTS if we do not know the nonce + * #GNUNET_DB_SUCCESS_HARD_ERROR on failure + */ + enum GNUNET_DB_QueryStatus + (*validate_solve_pin)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + uint32_t new_pin, + bool *solved); + + + /** + * 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_url set to URL of client (from client registration) + * @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_url set to client redirect URL + * @return transaction status: + * #GNUNET_DB_SUCCESS_ONE_RESULT if the nonce was found + * #GNUNET_DB_SUCCESS_NO_RESULTS if we do not know the nonce + * #GNUNET_DB_SUCCESS_HARD_ERROR on failure + */ + enum GNUNET_DB_QueryStatus + (*validate_get)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + char **client_url, + char **client_secret, + char **address, + char **client_scope, + char **client_state, + char **client_redirect_url); + + }; #endif