summaryrefslogtreecommitdiff
path: root/src/backend/anastasis-httpd_truth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/anastasis-httpd_truth.c')
-rw-r--r--src/backend/anastasis-httpd_truth.c374
1 files changed, 257 insertions, 117 deletions
diff --git a/src/backend/anastasis-httpd_truth.c b/src/backend/anastasis-httpd_truth.c
index 4dd3ddc..aedd0a2 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -882,6 +882,38 @@ return_key_share (
/**
+ * Mark @a gc as suspended and update the respective
+ * data structures and jobs.
+ *
+ * @param[in,out] gc context of the suspended operation
+ */
+static void
+gc_suspended (struct GetContext *gc)
+{
+ gc->suspended = true;
+ if (NULL == AH_to_heap)
+ AH_to_heap = GNUNET_CONTAINER_heap_create (
+ GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
+ gc,
+ gc->timeout.abs_value_us);
+ if (NULL != to_task)
+ {
+ GNUNET_SCHEDULER_cancel (to_task);
+ to_task = NULL;
+ }
+ {
+ struct GetContext *rn;
+
+ rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
+ to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
+ &do_timeout,
+ NULL);
+ }
+}
+
+
+/**
* Run the authorization method-specific 'process' function and continue
* based on its result with generating an HTTP response.
*
@@ -923,26 +955,7 @@ run_authorization_process (struct MHD_Connection *connection,
return MHD_YES;
case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
/* connection was suspended */
- gc->suspended = true;
- if (NULL == AH_to_heap)
- AH_to_heap = GNUNET_CONTAINER_heap_create (
- GNUNET_CONTAINER_HEAP_ORDER_MIN);
- gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap,
- gc,
- gc->timeout.abs_value_us);
- if (NULL != to_task)
- {
- GNUNET_SCHEDULER_cancel (to_task);
- to_task = NULL;
- }
- {
- struct GetContext *rn;
-
- rn = GNUNET_CONTAINER_heap_peek (AH_to_heap);
- to_task = GNUNET_SCHEDULER_add_at (rn->timeout,
- &do_timeout,
- NULL);
- }
+ gc_suspended (gc);
return MHD_YES;
case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
/* Challenge sent successfully */
@@ -978,6 +991,212 @@ run_authorization_process (struct MHD_Connection *connection,
}
+/**
+ * Use the database to rate-limit queries to the
+ * authentication procedure, but without actually
+ * storing 'real' challenge codes.
+ *
+ * @param[in,out] gc context to rate limit requests for
+ * @return #GNUNET_OK if rate-limiting passes,
+ * #GNUNET_NO if a reply was sent (rate limited)
+ * #GNUNET_SYSERR if we failed and no reply
+ * was queued
+ */
+static enum GNUNET_GenericReturnValue
+rate_limit (struct GetContext *gc)
+{
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_TIME_Absolute rt;
+ uint64_t code;
+ enum ANASTASIS_DB_CodeStatus cs;
+ struct GNUNET_HashCode hc;
+ bool satisfied;
+ uint64_t dummy;
+
+ rt = GNUNET_TIME_UNIT_FOREVER_ABS;
+ qs = db->create_challenge_code (db->cls,
+ &gc->truth_uuid,
+ MAX_QUESTION_FREQ,
+ GNUNET_TIME_UNIT_HOURS,
+ INITIAL_RETRY_COUNTER,
+ &rt,
+ &code);
+ if (0 > qs)
+ {
+ GNUNET_break (0 < qs);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "create_challenge_code (for rate limiting)"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_TOO_MANY_REQUESTS,
+ TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
+ NULL))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ /* decrement trial counter */
+ ANASTASIS_hash_answer (code + 1, /* always use wrong answer */
+ &hc);
+ cs = db->verify_challenge_code (db->cls,
+ &gc->truth_uuid,
+ &hc,
+ &dummy,
+ &satisfied);
+ switch (cs)
+ {
+ case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
+ /* good, what we wanted */
+ return GNUNET_OK;
+ case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
+ case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "verify_challenge_code"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_TOO_MANY_REQUESTS,
+ TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
+ NULL))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
+ /* this should be impossible, we used code+1 */
+ GNUNET_assert (0);
+ }
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Handle special case of a security question where we do not
+ * generate a code. Rate limits answers against brute forcing.
+ *
+ * @param[in,out] gc request to handle
+ * @param decrypted_truth hash to check against
+ * @param decrypted_truth_size number of bytes in @a decrypted_truth
+ * @return MHD status code
+ */
+static MHD_RESULT
+handle_security_question (struct GetContext *gc,
+ const void *decrypted_truth,
+ size_t decrypted_truth_size)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling security question challenge\n");
+ if (! gc->have_response)
+ {
+ return TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
+ NULL);
+ }
+ /* rate limit */
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = rate_limit (gc);
+ if (GNUNET_OK != ret)
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ /* check reply matches truth */
+ if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
+ (0 != memcmp (&gc->challenge_response,
+ decrypted_truth,
+ decrypted_truth_size)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Wrong answer provided to secure question had %u bytes, wanted %u\n",
+ (unsigned int) decrypted_truth_size,
+ (unsigned int) sizeof (struct GNUNET_HashCode));
+ return TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
+ NULL);
+ }
+ /* good, return the key share */
+ return return_key_share (&gc->truth_uuid,
+ gc->connection);
+}
+
+
+/**
+ * Handle special case of an answer being directly checked by the
+ * plugin and not by our database. Rate limits answers against brute
+ * forcing.
+ *
+ * @param[in,out] gc request to handle
+ * @param decrypted_truth hash to check against
+ * @param decrypted_truth_size number of bytes in @a decrypted_truth
+ * @return MHD status code
+ */
+static MHD_RESULT
+direct_validation (struct GetContext *gc,
+ const void *decrypted_truth,
+ size_t decrypted_truth_size)
+{
+ /* Non-random code, call plugin directly! */
+ enum ANASTASIS_AUTHORIZATION_Result aar;
+ enum GNUNET_GenericReturnValue res;
+
+ res = rate_limit (gc);
+ if (GNUNET_OK != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ gc->as = gc->authorization->start (gc->authorization->cls,
+ &AH_trigger_daemon,
+ NULL,
+ &gc->truth_uuid,
+ 0LLU,
+ decrypted_truth,
+ decrypted_truth_size);
+ if (NULL == gc->as)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (gc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
+ NULL);
+ }
+ aar = gc->authorization->process (gc->as,
+ GNUNET_TIME_UNIT_ZERO_ABS,
+ gc->connection);
+ switch (aar)
+ {
+ case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
+ GNUNET_break (0);
+ return MHD_YES;
+ case ANASTASIS_AUTHORIZATION_RES_FAILED:
+ return MHD_YES;
+ case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
+ gc_suspended (gc);
+ return MHD_YES;
+ case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
+ GNUNET_break (0);
+ return MHD_NO;
+ case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
+ return MHD_NO;
+ case ANASTASIS_AUTHORIZATION_RES_FINISHED:
+ return return_key_share (&gc->truth_uuid,
+ gc->connection);
+ }
+ GNUNET_break (0);
+ return MHD_NO;
+}
+
+
MHD_RESULT
AH_handler_truth_get (
struct MHD_Connection *connection,
@@ -1113,7 +1332,6 @@ AH_handler_truth_get (
GNUNET_TIME_UNIT_SECONDS);
}
}
-
} /* end of first-time initialization (if NULL == gc) */
else
{
@@ -1291,104 +1509,14 @@ AH_handler_truth_get (
but check that the hash matches */
if (is_question)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling security question challenge\n");
- if (! gc->have_response)
- {
- GNUNET_free (decrypted_truth);
- GNUNET_free (truth_mime);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
- NULL);
- }
+ MHD_RESULT ret;
- {
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_TIME_Absolute rt;
- uint64_t code;
- enum ANASTASIS_DB_CodeStatus cs;
- struct GNUNET_HashCode hc;
- bool satisfied;
- uint64_t dummy;
-
- rt = GNUNET_TIME_UNIT_FOREVER_ABS;
- qs = db->create_challenge_code (db->cls,
- &gc->truth_uuid,
- MAX_QUESTION_FREQ,
- GNUNET_TIME_UNIT_HOURS,
- INITIAL_RETRY_COUNTER,
- &rt,
- &code);
- if (0 > qs)
- {
- GNUNET_break (0 < qs);
- GNUNET_free (decrypted_truth);
- GNUNET_free (truth_mime);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "create_challenge_code (for rate limiting)");
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- GNUNET_free (decrypted_truth);
- GNUNET_free (truth_mime);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_TOO_MANY_REQUESTS,
- TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
- NULL);
- }
- /* decrement trial counter */
- ANASTASIS_hash_answer (code + 1, /* always use wrong answer */
- &hc);
- cs = db->verify_challenge_code (db->cls,
- &gc->truth_uuid,
- &hc,
- &dummy,
- &satisfied);
- switch (cs)
- {
- case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
- /* good, what we wanted */
- break;
- case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
- case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (gc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "verify_challenge_code");
- case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_TOO_MANY_REQUESTS,
- TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
- NULL);
- case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
- /* this should be impossible, we used code+1 */
- GNUNET_assert (0);
- }
- }
- if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) ||
- (0 != memcmp (&gc->challenge_response,
- decrypted_truth,
- decrypted_truth_size)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Wrong answer provided to secure question had %u bytes, wanted %u\n",
- (unsigned int) decrypted_truth_size,
- (unsigned int) sizeof (struct GNUNET_HashCode));
- GNUNET_free (decrypted_truth);
- GNUNET_free (truth_mime);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
- NULL);
- }
- GNUNET_free (decrypted_truth);
+ ret = handle_security_question (gc,
+ decrypted_truth,
+ decrypted_truth_size);
GNUNET_free (truth_mime);
- return return_key_share (&gc->truth_uuid,
- connection);
+ GNUNET_free (decrypted_truth);
+ return ret;
}
/* Not security question, check for answer in DB */
@@ -1399,6 +1527,18 @@ AH_handler_truth_get (
uint64_t code;
GNUNET_free (truth_mime);
+ if (gc->authorization->user_provided_code)
+ {
+ MHD_RESULT res;
+
+ res = direct_validation (gc,
+ decrypted_truth,
+ decrypted_truth_size);
+ GNUNET_free (decrypted_truth);
+ return res;
+ }
+
+ /* random code, check against database */
cs = db->verify_challenge_code (db->cls,
&gc->truth_uuid,
&gc->challenge_response,