summaryrefslogtreecommitdiff
path: root/src/reducer
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer')
-rw-r--r--src/reducer/Makefile.am5
-rw-r--r--src/reducer/anastasis_api_backup_redux.c658
-rw-r--r--src/reducer/anastasis_api_discovery.c549
-rw-r--r--src/reducer/anastasis_api_providers.c300
-rw-r--r--src/reducer/anastasis_api_recovery_redux.c1863
-rw-r--r--src/reducer/anastasis_api_redux.c735
-rw-r--r--src/reducer/anastasis_api_redux.h68
-rw-r--r--src/reducer/validation_CH_AHV.c6
-rw-r--r--src/reducer/validation_CZ_BN.c6
-rw-r--r--src/reducer/validation_DE_SVN.c6
-rw-r--r--src/reducer/validation_DE_TIN.c6
-rw-r--r--src/reducer/validation_ES_DNI.c184
-rw-r--r--src/reducer/validation_FR_INSEE.c66
-rw-r--r--src/reducer/validation_IN_AADHAR.c6
-rw-r--r--src/reducer/validation_IT_CF.c6
-rw-r--r--src/reducer/validation_NL_BSN.c57
-rw-r--r--src/reducer/validation_XX_SQUARE.c6
-rw-r--r--src/reducer/validation_XY_PRIME.c6
18 files changed, 3101 insertions, 1432 deletions
diff --git a/src/reducer/Makefile.am b/src/reducer/Makefile.am
index 5cbe6f7..1536d21 100644
--- a/src/reducer/Makefile.am
+++ b/src/reducer/Makefile.am
@@ -15,6 +15,8 @@ libanastasisredux_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libanastasisredux_la_SOURCES = \
+ anastasis_api_discovery.c \
+ anastasis_api_providers.c \
anastasis_api_redux.c anastasis_api_redux.h \
anastasis_api_recovery_redux.c \
anastasis_api_backup_redux.c \
@@ -22,8 +24,11 @@ libanastasisredux_la_SOURCES = \
validation_CZ_BN.c \
validation_DE_SVN.c \
validation_DE_TIN.c \
+ validation_ES_DNI.c \
+ validation_FR_INSEE.c \
validation_IN_AADHAR.c \
validation_IT_CF.c \
+ validation_NL_BSN.c \
validation_XX_SQUARE.c \
validation_XY_PRIME.c
libanastasisredux_la_LIBADD = \
diff --git a/src/reducer/anastasis_api_backup_redux.c b/src/reducer/anastasis_api_backup_redux.c
index cfef852..6ca6de7 100644
--- a/src/reducer/anastasis_api_backup_redux.c
+++ b/src/reducer/anastasis_api_backup_redux.c
@@ -1,16 +1,16 @@
/*
This file is part of Anastasis
- Copyright (C) 2020, 2021 Anastasis SARL
+ Copyright (C) 2020-2023 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
@@ -32,7 +32,7 @@
* anastasis-httpd.h.
*/
#define ANASTASIS_FREE_STORAGE GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_YEARS, 5)
+ GNUNET_TIME_UNIT_YEARS, 5)
/**
* CPU limiter: do not evaluate more than 16k
@@ -131,7 +131,7 @@ ANASTASIS_backup_state_from_string_ (const char *state_string)
if (0 == strcmp (state_string,
backup_strings[i]))
return i;
- return ANASTASIS_BACKUP_STATE_ERROR;
+ return ANASTASIS_BACKUP_STATE_INVALID;
}
@@ -177,11 +177,78 @@ json_t *
ANASTASIS_backup_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
json_t *initial_state;
+ const char *external_reducer = ANASTASIS_REDUX_probe_external_reducer ();
+
+ if (NULL != external_reducer)
+ {
+ int pipefd_stdout[2];
+ pid_t pid = 0;
+ int status;
+ FILE *reducer_stdout;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Using external reducer '%s' for backup start status\n",
+ external_reducer);
+
+ GNUNET_assert (0 == pipe (pipefd_stdout));
+ pid = fork ();
+ if (pid == 0)
+ {
+ close (pipefd_stdout[0]);
+ dup2 (pipefd_stdout[1], STDOUT_FILENO);
+ execlp (external_reducer,
+ external_reducer,
+ "-b",
+ NULL);
+ GNUNET_assert (0);
+ }
+
+ close (pipefd_stdout[1]);
+ reducer_stdout = fdopen (pipefd_stdout[0],
+ "r");
+ {
+ json_error_t err;
+
+ initial_state = json_loadf (reducer_stdout,
+ 0,
+ &err);
+
+ if (NULL == initial_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "External reducer did not output valid JSON: %s:%d:%d %s\n",
+ err.source,
+ err.line,
+ err.column,
+ err.text);
+ GNUNET_assert (0 == fclose (reducer_stdout));
+ waitpid (pid, &status, 0);
+ return NULL;
+ }
+ }
+
+ GNUNET_assert (NULL != initial_state);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Waiting for external reducer to terminate.\n");
+ GNUNET_assert (0 == fclose (reducer_stdout));
+ reducer_stdout = NULL;
+ waitpid (pid, &status, 0);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "External reducer finished with exit status '%d'\n",
+ status);
+ return initial_state;
+ }
(void) cfg;
initial_state = ANASTASIS_REDUX_load_continents_ ();
if (NULL == initial_state)
return NULL;
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (initial_state,
+ "reducer_type",
+ json_string ("backup")));
set_state (initial_state,
ANASTASIS_BACKUP_STATE_CONTINENT_SELECTING);
return initial_state;
@@ -286,22 +353,30 @@ add_authentication (json_t *state,
json_object_foreach (auth_providers, url, details)
{
- json_t *methods;
+ const json_t *methods = NULL;
json_t *method;
size_t index;
- uint32_t size_limit_in_mb;
+ uint32_t size_limit_in_mb = 0;
+ const char *status;
+ uint32_t http_status = 0;
struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
- &size_limit_in_mb),
- GNUNET_JSON_spec_json ("methods",
- &methods),
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
+ &size_limit_in_mb),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("methods",
+ &methods),
+ NULL),
GNUNET_JSON_spec_end ()
};
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (details,
- "http_status")))
- continue; /* skip providers that are down */
if (GNUNET_OK !=
GNUNET_JSON_parse (details,
ispec,
@@ -310,6 +385,17 @@ add_authentication (json_t *state,
GNUNET_break (0);
continue;
}
+ if (0 != strcmp (status,
+ "ok"))
+ continue;
+ if (MHD_HTTP_OK != http_status)
+ continue; /* skip providers that are down */
+ if ( (NULL == methods) ||
+ (0 == size_limit_in_mb) )
+ {
+ GNUNET_break (0);
+ continue;
+ }
json_array_foreach (methods, index, method)
{
const char *type;
@@ -325,7 +411,6 @@ add_authentication (json_t *state,
break;
}
}
- GNUNET_JSON_parse_free (ispec);
if (! challenge_size_ok (size_limit_in_mb,
challenge_size))
{
@@ -681,17 +766,20 @@ free_costs (struct Costs *costs)
* Check if providers @a p1 and @a p2 have equivalent
* methods and cost structures.
*
+ * @param pb policy builder with list of providers
+ * @param p1 name of provider to compare
+ * @param p2 name of provider to compare
* @return true if the providers are fully equivalent
*/
static bool
-equiv_provider (struct PolicyBuilder *pb,
+equiv_provider (const struct PolicyBuilder *pb,
const char *p1,
const char *p2)
{
- json_t *j1;
- json_t *j2;
- json_t *m1;
- json_t *m2;
+ const json_t *j1;
+ const json_t *j2;
+ const json_t *m1;
+ const json_t *m2;
struct TALER_Amount uc1;
struct TALER_Amount uc2;
@@ -708,8 +796,8 @@ equiv_provider (struct PolicyBuilder *pb,
{
struct GNUNET_JSON_Specification s1[] = {
- GNUNET_JSON_spec_json ("methods",
- &m1),
+ GNUNET_JSON_spec_array_const ("methods",
+ &m1),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&uc1),
GNUNET_JSON_spec_end ()
@@ -727,8 +815,8 @@ equiv_provider (struct PolicyBuilder *pb,
{
struct GNUNET_JSON_Specification s2[] = {
- GNUNET_JSON_spec_json ("methods",
- &m2),
+ GNUNET_JSON_spec_array_const ("methods",
+ &m2),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&uc2),
GNUNET_JSON_spec_end ()
@@ -847,7 +935,7 @@ eval_provider_selection (struct PolicyBuilder *pb,
pb->m_idx[i]);
const json_t *provider_cfg = json_object_get (pb->providers,
prov_sel[i]);
- json_t *provider_methods;
+ const json_t *provider_methods;
const char *method_type;
json_t *md;
size_t index;
@@ -857,8 +945,8 @@ eval_provider_selection (struct PolicyBuilder *pb,
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
&size_limit_in_mb),
- GNUNET_JSON_spec_json ("methods",
- &provider_methods),
+ GNUNET_JSON_spec_array_const ("methods",
+ &provider_methods),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&upload_cost),
GNUNET_JSON_spec_end ()
@@ -930,7 +1018,6 @@ eval_provider_selection (struct PolicyBuilder *pb,
GNUNET_break (0);
pb->ec = TALER_EC_ANASTASIS_REDUCER_STATE_INVALID;
pb->hint = "'methods' of provider";
- GNUNET_JSON_parse_free (pspec);
for (unsigned int i = 0; i<pb->req_methods; i++)
free_costs (policy_ent[i].usage_fee);
return;
@@ -952,14 +1039,12 @@ eval_provider_selection (struct PolicyBuilder *pb,
{
/* Provider does not OFFER this method, combination not possible.
Cost is basically 'infinite', but we simply then skip this. */
- GNUNET_JSON_parse_free (pspec);
GNUNET_JSON_parse_free (mspec);
for (unsigned int i = 0; i<pb->req_methods; i++)
free_costs (policy_ent[i].usage_fee);
return;
}
GNUNET_JSON_parse_free (mspec);
- GNUNET_JSON_parse_free (pspec);
}
/* calculate provider diversity by counting number of different
@@ -1082,6 +1167,34 @@ provider_candidate (struct PolicyBuilder *pb,
json_object_foreach (pb->providers, url, pconfig)
{
+ const char *status;
+ uint32_t http_status = 0;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (pconfig,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if ( (MHD_HTTP_OK != http_status) ||
+ (0 == strcmp (status,
+ "disabled")) )
+ {
+ GNUNET_JSON_parse_free (spec);
+ continue;
+ }
+ GNUNET_JSON_parse_free (spec);
prov_sel[i] = url;
if (i == pb->req_methods - 1)
{
@@ -1171,58 +1284,6 @@ method_candidate (struct PolicyBuilder *pb,
/**
- * Lookup @a salt of @a provider_url in @a state.
- *
- * @param state the state to inspect
- * @param provider_url provider to look into
- * @param[out] salt value to extract
- * @return #GNUNET_OK on success
- */
-static int
-lookup_salt (const json_t *state,
- const char *provider_url,
- struct ANASTASIS_CRYPTO_ProviderSaltP *salt)
-{
- const json_t *aps;
- const json_t *cfg;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("salt",
- salt),
- GNUNET_JSON_spec_end ()
- };
-
- aps = json_object_get (state,
- "authentication_providers");
- if (NULL == aps)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- cfg = json_object_get (aps,
- provider_url);
- if (NULL == cfg)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (cfg,
- "http_status")))
- return GNUNET_NO; /* skip providers that are down */
- if (GNUNET_OK !=
- GNUNET_JSON_parse (cfg,
- spec,
- NULL, NULL))
- {
- /* provider not working */
- GNUNET_break_op (0);
- return GNUNET_NO;
- }
- return GNUNET_OK;
-}
-
-
-/**
* Compare two cost lists.
*
* @param my cost to compare
@@ -1604,7 +1665,8 @@ done_authentication (json_t *state,
pb.methods = json_object_get (state,
"authentication_methods");
if ( (NULL == pb.methods) ||
- (! json_is_array (pb.methods)) )
+ (! json_is_array (pb.methods)) ||
+ (json_array_size (pb.methods) > UINT_MAX) )
{
ANASTASIS_redux_fail_ (cb,
cb_cls,
@@ -1612,7 +1674,8 @@ done_authentication (json_t *state,
"'authentication_methods' must be provided");
return NULL;
}
- pb.num_methods = json_array_size (pb.methods);
+ pb.num_methods
+ = (unsigned int) json_array_size (pb.methods);
switch (pb.num_methods)
{
case 0:
@@ -1622,6 +1685,11 @@ done_authentication (json_t *state,
"'authentication_methods' must not be empty");
return NULL;
case 1:
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "Two factor authentication (2-FA) is required");
+ return NULL;
case 2:
pb.req_methods = pb.num_methods;
break;
@@ -1685,9 +1753,9 @@ done_authentication (json_t *state,
struct ANASTASIS_CRYPTO_ProviderSaltP salt;
if (GNUNET_OK !=
- lookup_salt (state,
- url,
- &salt))
+ ANASTASIS_reducer_lookup_salt (state,
+ url,
+ &salt))
continue; /* skip providers that are down */
provider = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("provider_url",
@@ -1714,9 +1782,9 @@ done_authentication (json_t *state,
url_str = json_string_value (url);
if ( (NULL == url_str) ||
(GNUNET_OK !=
- lookup_salt (state,
- url_str,
- &salt)) )
+ ANASTASIS_reducer_lookup_salt (state,
+ url_str,
+ &salt)) )
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (cb,
@@ -1877,8 +1945,8 @@ add_policy (json_t *state,
{
const char *provider_url;
uint32_t method_idx;
- json_t *prov_methods;
const char *method_type;
+ const json_t *prov_methods;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("provider",
&provider_url),
@@ -1904,11 +1972,19 @@ add_policy (json_t *state,
{
const json_t *prov_cfg;
uint32_t limit;
+ const char *status;
+ uint32_t http_status = 0;
struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
&limit),
- GNUNET_JSON_spec_json ("methods",
- &prov_methods),
+ GNUNET_JSON_spec_array_const ("methods",
+ &prov_methods),
GNUNET_JSON_spec_end ()
};
@@ -1924,10 +2000,6 @@ add_policy (json_t *state,
"provider URL unknown");
return NULL;
}
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (prov_cfg,
- "http_status")))
- continue;
if (GNUNET_OK !=
GNUNET_JSON_parse (prov_cfg,
spec,
@@ -1937,16 +2009,13 @@ add_policy (json_t *state,
json_decref (methods);
continue;
}
- if (! json_is_array (prov_methods))
+ if ( (MHD_HTTP_OK != http_status) ||
+ (0 != strcmp (status,
+ "ok")) )
{
- GNUNET_break (0);
+ /* skip provider, disabled or down */
json_decref (methods);
- json_decref (prov_methods);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "provider lacks authentication methods");
- return NULL;
+ continue;
}
}
@@ -1959,7 +2028,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
@@ -1972,7 +2040,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
@@ -2004,7 +2071,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
@@ -2021,7 +2087,6 @@ add_policy (json_t *state,
{
GNUNET_break (0);
json_decref (methods);
- json_decref (prov_methods);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
@@ -2032,7 +2097,6 @@ add_policy (json_t *state,
GNUNET_assert (0 ==
json_array_append (methods,
method));
- json_decref (prov_methods);
} /* end of json_array_foreach (arg_array, index, method) */
}
@@ -2323,12 +2387,12 @@ del_challenge (json_t *state,
* @return number of years of service to pay for
*/
static unsigned int
-expiration_to_years (struct GNUNET_TIME_Absolute expiration)
+expiration_to_years (struct GNUNET_TIME_Timestamp expiration)
{
struct GNUNET_TIME_Relative rem;
unsigned int years;
- rem = GNUNET_TIME_absolute_get_remaining (expiration);
+ rem = GNUNET_TIME_absolute_get_remaining (expiration.abs_time);
years = rem.rel_value_us / GNUNET_TIME_UNIT_YEARS.rel_value_us;
if (0 != rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)
years++;
@@ -2348,7 +2412,7 @@ expiration_to_years (struct GNUNET_TIME_Absolute expiration)
*/
static enum GNUNET_GenericReturnValue
update_expiration_cost (json_t *state,
- struct GNUNET_TIME_Absolute expiration)
+ struct GNUNET_TIME_Timestamp expiration)
{
struct Costs *costs = NULL;
unsigned int years;
@@ -2373,26 +2437,33 @@ update_expiration_cost (json_t *state,
json_object_foreach (providers, url, provider)
{
struct TALER_Amount annual_fee;
+ const char *status;
+ uint32_t http_status = 0;
struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
TALER_JSON_spec_amount_any ("annual_fee",
&annual_fee),
GNUNET_JSON_spec_end ()
};
struct TALER_Amount fee;
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (provider,
- "http_status")))
- continue; /* skip providers that are down */
if (GNUNET_OK !=
GNUNET_JSON_parse (provider,
pspec,
NULL, NULL))
{
- /* strange, skip as well */
- GNUNET_break_op (0);
+ /* likely down, skip */
continue;
}
+ if ( (MHD_HTTP_OK != http_status) ||
+ (0 != strcmp (status,
+ "ok")) )
+ continue; /* skip providers that are down or disabled */
if (0 >
TALER_amount_multiply (&fee,
&annual_fee,
@@ -2473,7 +2544,15 @@ update_expiration_cost (json_t *state,
off++;
{
struct TALER_Amount upload_cost;
+ const char *status;
+ uint32_t http_status = 0;
struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
TALER_JSON_spec_amount_any ("truth_upload_fee",
&upload_cost),
GNUNET_JSON_spec_end ()
@@ -2491,6 +2570,13 @@ update_expiration_cost (json_t *state,
GNUNET_break (0);
return GNUNET_SYSERR;
}
+ if ( (MHD_HTTP_OK != http_status) ||
+ (0 != strcmp (status,
+ "ok")) )
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
if (0 >
TALER_amount_multiply (&fee,
&upload_cost,
@@ -2519,8 +2605,7 @@ update_expiration_cost (json_t *state,
{
struct Costs *nxt = costs->next;
- if ( (0 != costs->cost.value) ||
- (0 != costs->cost.fraction) )
+ if (! TALER_amount_is_zero (&costs->cost))
{
json_t *ao;
@@ -2542,13 +2627,12 @@ update_expiration_cost (json_t *state,
}
if (is_free)
- expiration = GNUNET_TIME_relative_to_absolute (ANASTASIS_FREE_STORAGE);
+ expiration = GNUNET_TIME_relative_to_timestamp (ANASTASIS_FREE_STORAGE);
/* update 'expiration' in state */
{
json_t *eo;
- (void) GNUNET_TIME_round_abs (&expiration);
- eo = GNUNET_JSON_from_time_abs (expiration);
+ eo = GNUNET_JSON_from_timestamp (expiration);
GNUNET_assert (0 ==
json_object_set_new (state,
"expiration",
@@ -2589,11 +2673,13 @@ done_policy_review (json_t *state,
return NULL;
}
{
- struct GNUNET_TIME_Absolute exp = {0};
+ struct GNUNET_TIME_Timestamp exp
+ = GNUNET_TIME_UNIT_ZERO_TS;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_absolute_time ("expiration",
- &exp)),
+ GNUNET_JSON_spec_timestamp ("expiration",
+ &exp),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -2608,8 +2694,8 @@ done_policy_review (json_t *state,
"invalid expiration specified");
return NULL;
}
- if (0 == exp.abs_value_us)
- exp = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_YEARS);
+ if (GNUNET_TIME_absolute_is_zero (exp.abs_time))
+ exp = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS);
if (GNUNET_OK !=
update_expiration_cost (state,
exp))
@@ -2869,6 +2955,62 @@ serialize_truth (struct UploadContext *uc)
/**
+ * Test if the given @a provider_url is used by any of the
+ * authentication methods and thus the provider should be
+ * considered mandatory for storing the policy.
+ *
+ * @param state state to inspect
+ * @param provider_url provider to test
+ * @return false if the provider can be removed from policy
+ * upload considerations without causing a problem
+ */
+static bool
+provider_required (const json_t *state,
+ const char *provider_url)
+{
+ json_t *policies
+ = json_object_get (state,
+ "policies");
+ size_t pidx;
+ json_t *policy;
+
+ json_array_foreach (policies, pidx, policy)
+ {
+ json_t *methods = json_object_get (policy,
+ "methods");
+ size_t midx;
+ json_t *method;
+
+ json_array_foreach (methods, midx, method)
+ {
+ const char *provider
+ = json_string_value (json_object_get (method,
+ "provider"));
+
+ if (NULL == provider)
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 == strcmp (provider,
+ provider_url))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * All truth uploads are done, begin with uploading the policy.
+ *
+ * @param[in,out] uc context for the operation
+ */
+static void
+share_secret (struct UploadContext *uc);
+
+
+/**
* Function called with the results of a #ANASTASIS_secret_share().
*
* @param cls closure with a `struct UploadContext *`
@@ -2901,8 +3043,8 @@ secret_share_result_cb (void *cls,
d = GNUNET_JSON_PACK (
GNUNET_JSON_pack_uint64 ("policy_version",
pssi->policy_version),
- GNUNET_JSON_pack_time_abs ("policy_expiration",
- pssi->policy_expiration));
+ GNUNET_JSON_pack_timestamp ("policy_expiration",
+ pssi->policy_expiration));
GNUNET_assert (NULL != d);
GNUNET_assert (0 ==
json_object_set_new (sa,
@@ -2952,7 +3094,8 @@ secret_share_result_cb (void *cls,
json_array_foreach (providers, off, provider)
{
const char *purl = json_string_value (json_object_get (provider,
- "provider_url"));
+ "provider_url")
+ );
if (NULL == purl)
{
@@ -2993,13 +3136,53 @@ secret_share_result_cb (void *cls,
{
json_t *details;
+ if (! provider_required (uc->state,
+ sr->details.provider_failure.provider_url))
+ {
+ /* try again without that provider */
+ json_t *provider;
+ json_t *providers;
+ size_t idx;
+
+ provider
+ = json_object_get (
+ json_object_get (uc->state,
+ "authentication_providers"),
+ sr->details.provider_failure.provider_url);
+ GNUNET_break (0 ==
+ json_object_set_new (provider,
+ "status",
+ json_string ("disabled")));
+ providers
+ = json_object_get (uc->state,
+ "policy_providers");
+ json_array_foreach (providers, idx, provider)
+ {
+ const char *url
+ = json_string_value (json_object_get (provider,
+ "provider_url"));
+
+ if ( (NULL != url) &&
+ (0 == strcmp (sr->details.provider_failure.provider_url,
+ url)) )
+ {
+ GNUNET_break (0 ==
+ json_array_remove (providers,
+ idx));
+ break;
+ }
+ }
+ share_secret (uc);
+ return;
+ }
details = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("backup_state",
- "ERROR"),
GNUNET_JSON_pack_uint64 ("http_status",
sr->details.provider_failure.http_status),
- GNUNET_JSON_pack_uint64 ("upload_status",
+ GNUNET_JSON_pack_uint64 ("code",
sr->details.provider_failure.ec),
+ GNUNET_JSON_pack_string ("hint",
+ TALER_ErrorCode_get_hint (
+ sr->details.provider_failure.ec)),
GNUNET_JSON_pack_string ("provider_url",
sr->details.provider_failure.provider_url));
uc->cb (uc->cb_cls,
@@ -3028,26 +3211,29 @@ secret_share_result_cb (void *cls,
static void
share_secret (struct UploadContext *uc)
{
- json_t *user_id;
- json_t *core_secret;
- json_t *jpolicies;
- json_t *providers = NULL;
+ const json_t *user_id;
+ const json_t *core_secret;
+ const json_t *jpolicies;
+ const json_t *providers = NULL;
size_t policies_len;
const char *secret_name = NULL;
unsigned int pds_len;
struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("identity_attributes",
- &user_id),
- GNUNET_JSON_spec_json ("policies",
- &jpolicies),
- GNUNET_JSON_spec_json ("policy_providers",
- &providers),
- GNUNET_JSON_spec_json ("core_secret",
- &core_secret),
+ GNUNET_JSON_spec_object_const ("identity_attributes",
+ &user_id),
+ GNUNET_JSON_spec_array_const ("policies",
+ &jpolicies),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("policy_providers",
+ &providers),
+ NULL),
+ GNUNET_JSON_spec_object_const ("core_secret",
+ &core_secret),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("secret_name",
- &secret_name)),
+ &secret_name),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -3069,12 +3255,13 @@ share_secret (struct UploadContext *uc)
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("timeout",
- &timeout)),
+ &timeout),
+ NULL),
GNUNET_JSON_spec_end ()
};
args = json_object_get (uc->state,
- "pay-arguments");
+ "pay_arguments");
if ( (NULL != args) &&
(GNUNET_OK !=
GNUNET_JSON_parse (args,
@@ -3094,40 +3281,43 @@ share_secret (struct UploadContext *uc)
}
}
- if ( (! json_is_object (user_id)) ||
- (! json_is_array (jpolicies)) ||
- (0 == json_array_size (jpolicies)) ||
- ( (NULL != providers) &&
- (! json_is_array (providers)) ) )
+ policies_len = json_array_size (jpolicies);
+ if (0 == policies_len)
{
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"State parsing failed checks when preparing to share secret");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
- policies_len = json_array_size (jpolicies);
- pds_len = json_array_size (providers);
-
+ if (json_array_size (providers) > UINT_MAX)
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (uc->cb,
+ uc->cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "provider array excessively long");
+ upload_cancel_cb (uc);
+ return;
+ }
+ pds_len
+ = (unsigned int) json_array_size (providers);
if (0 == pds_len)
{
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"no workable providers in state");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
-
{
struct ANASTASIS_Policy *vpolicies[policies_len];
const struct ANASTASIS_Policy *policies[policies_len];
- struct ANASTASIS_ProviderDetails pds[GNUNET_NZL (pds_len)];
+ struct ANASTASIS_ProviderDetails pds[pds_len];
/* initialize policies/vpolicies arrays */
memset (pds,
@@ -3142,18 +3332,19 @@ share_secret (struct UploadContext *uc)
unsigned int methods_len;
if ( (! json_is_array (jmethods)) ||
- (0 == json_array_size (jmethods)) )
+ (0 == json_array_size (jmethods)) ||
+ (json_array_size (jmethods) > UINT_MAX) )
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (uc->cb,
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'methods' must be an array");
- GNUNET_JSON_parse_free (spec);
+ "'methods' must be an array of sane length");
upload_cancel_cb (uc);
return;
}
- methods_len = json_array_size (jmethods);
+ methods_len
+ = (unsigned int) json_array_size (jmethods);
{
struct ANASTASIS_Policy *p;
struct ANASTASIS_Truth *truths[methods_len];
@@ -3163,13 +3354,14 @@ share_secret (struct UploadContext *uc)
{
const json_t *jmethod = json_array_get (jmethods,
j);
- json_t *jtruth = NULL;
+ const json_t *jtruth = NULL;
uint32_t truth_index;
const char *provider_url;
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("truth",
- &jtruth)),
+ GNUNET_JSON_spec_object_const ("truth",
+ &jtruth),
+ NULL),
GNUNET_JSON_spec_string ("provider",
&provider_url),
GNUNET_JSON_spec_uint32 ("authentication_method",
@@ -3190,7 +3382,6 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
@@ -3207,8 +3398,6 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (ispec);
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
@@ -3246,13 +3435,10 @@ share_secret (struct UploadContext *uc)
uc->cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'truth' failed to decode");
- GNUNET_JSON_parse_free (ispec);
- GNUNET_JSON_parse_free (spec);
upload_cancel_cb (uc);
return;
}
}
- GNUNET_JSON_parse_free (ispec);
ctruths[j] = truths[j];
}
p = ANASTASIS_policy_create (ctruths,
@@ -3272,7 +3458,8 @@ share_secret (struct UploadContext *uc)
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("payment_secret",
- &pds[i].payment_secret)),
+ &pds[i].payment_secret),
+ NULL),
GNUNET_JSON_spec_string ("provider_url",
&pds[i].provider_url),
GNUNET_JSON_spec_end ()
@@ -3283,9 +3470,9 @@ share_secret (struct UploadContext *uc)
ispec,
NULL, NULL)) ||
(GNUNET_OK !=
- lookup_salt (uc->state,
- pds[i].provider_url,
- &pds[i].provider_salt)) )
+ ANASTASIS_reducer_lookup_salt (uc->state,
+ pds[i].provider_url,
+ &pds[i].provider_salt)) )
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (uc->cb,
@@ -3295,7 +3482,6 @@ share_secret (struct UploadContext *uc)
for (unsigned int i = 0; i<policies_len; i++)
ANASTASIS_policy_destroy (vpolicies[i]);
upload_cancel_cb (uc);
- GNUNET_JSON_parse_free (spec);
return;
}
}
@@ -3326,7 +3512,6 @@ share_secret (struct UploadContext *uc)
for (unsigned int i = 0; i<policies_len; i++)
ANASTASIS_policy_destroy (vpolicies[i]);
}
- GNUNET_JSON_parse_free (spec);
if (NULL == uc->ss)
{
GNUNET_break (0);
@@ -3519,7 +3704,8 @@ add_truth_object (struct UploadContext *uc,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint32 ("upload_status",
- &status)),
+ &status),
+ NULL),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
@@ -3568,9 +3754,9 @@ add_truth_object (struct UploadContext *uc,
};
if (GNUNET_OK !=
- lookup_salt (uc->state,
- provider_url,
- &salt))
+ ANASTASIS_reducer_lookup_salt (uc->state,
+ provider_url,
+ &salt))
{
GNUNET_break (0);
return GNUNET_SYSERR;
@@ -3703,10 +3889,12 @@ check_truth_upload (struct UploadContext *uc,
&type),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("mime_type",
- &mime_type)),
+ &mime_type),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("instructions",
- &instructions)),
+ &instructions),
+ NULL),
GNUNET_JSON_spec_varsize ("challenge",
&truth_data,
&truth_data_size),
@@ -3737,9 +3925,9 @@ check_truth_upload (struct UploadContext *uc,
tue->am_idx = am_idx;
tue->policies_length = 1;
if (GNUNET_OK !=
- lookup_salt (uc->state,
- provider_url,
- &provider_salt))
+ ANASTASIS_reducer_lookup_salt (uc->state,
+ provider_url,
+ &provider_salt))
{
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
@@ -3755,7 +3943,6 @@ check_truth_upload (struct UploadContext *uc,
struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
struct ANASTASIS_CRYPTO_KeyShareP key_share;
struct ANASTASIS_CRYPTO_NonceP nonce;
-
struct GNUNET_JSON_Specification jspec[] = {
GNUNET_JSON_spec_fixed_auto ("salt",
&question_salt),
@@ -3841,10 +4028,10 @@ upload (json_t *state,
struct UploadContext *uc;
json_t *auth_methods;
json_t *policies;
- struct GNUNET_TIME_Absolute expiration;
+ struct GNUNET_TIME_Timestamp expiration;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_absolute_time ("expiration",
- &expiration),
+ GNUNET_JSON_spec_timestamp ("expiration",
+ &expiration),
GNUNET_JSON_spec_end ()
};
@@ -3895,12 +4082,13 @@ upload (json_t *state,
struct GNUNET_JSON_Specification pspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("timeout",
- &uc->timeout)),
+ &uc->timeout),
+ NULL),
GNUNET_JSON_spec_end ()
};
args = json_object_get (uc->state,
- "pay-arguments");
+ "pay_arguments");
if ( (NULL != args) &&
(GNUNET_OK !=
GNUNET_JSON_parse (args,
@@ -3954,7 +4142,8 @@ upload (json_t *state,
&am_idx),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("truth",
- &truth)),
+ &truth),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -4111,17 +4300,23 @@ check_upload_size_limit (json_t *state,
see #6760. */
json_object_foreach (aps, url, ap)
{
- uint32_t limit;
+ uint32_t limit = 0;
+ const char *status;
+ uint32_t http_status = 0;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
- &limit),
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("storage_limit_in_megabytes",
+ &limit),
+ NULL),
GNUNET_JSON_spec_end ()
};
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (ap,
- "http_status")))
- continue; /* skip providers that are down */
if (GNUNET_OK !=
GNUNET_JSON_parse (ap,
spec,
@@ -4131,6 +4326,10 @@ check_upload_size_limit (json_t *state,
GNUNET_break_op (0);
continue;
}
+ if ( (MHD_HTTP_OK != http_status) ||
+ (0 != strcmp (status,
+ "ok")) )
+ continue;
if (0 == limit)
return GNUNET_SYSERR;
min_limit = GNUNET_MIN (min_limit,
@@ -4161,13 +4360,15 @@ enter_secret (json_t *state,
void *cb_cls)
{
json_t *jsecret;
- struct GNUNET_TIME_Absolute expiration = {0};
+ struct GNUNET_TIME_Timestamp expiration
+ = GNUNET_TIME_UNIT_ZERO_TS;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("secret",
&jsecret),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_absolute_time ("expiration",
- &expiration)),
+ GNUNET_JSON_spec_timestamp ("expiration",
+ &expiration),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -4215,7 +4416,7 @@ enter_secret (json_t *state,
break;
}
}
- if (0 != expiration.abs_value_us)
+ if (! GNUNET_TIME_absolute_is_zero (expiration.abs_time))
{
if (GNUNET_OK !=
update_expiration_cost (state,
@@ -4346,10 +4547,10 @@ update_expiration (json_t *state,
ANASTASIS_ActionCallback cb,
void *cb_cls)
{
- struct GNUNET_TIME_Absolute expiration;
+ struct GNUNET_TIME_Timestamp expiration;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_absolute_time ("expiration",
- &expiration),
+ GNUNET_JSON_spec_timestamp ("expiration",
+ &expiration),
GNUNET_JSON_spec_end ()
};
@@ -4483,7 +4684,7 @@ pay_truths_backup (json_t *state,
if (NULL != arguments)
GNUNET_assert (0 ==
json_object_set (state,
- "pay-arguments",
+ "pay_arguments",
(json_t *) arguments));
return upload (state,
cb,
@@ -4513,7 +4714,7 @@ pay_policies_backup (json_t *state,
if (NULL != arguments)
GNUNET_assert (0 ==
json_object_set (state,
- "pay-arguments",
+ "pay_arguments",
(json_t *) arguments));
return upload (state,
cb,
@@ -4596,6 +4797,11 @@ ANASTASIS_backup_action_ (json_t *state,
},
{
ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
+ "poll_providers",
+ &ANASTASIS_REDUX_poll_providers_
+ },
+ {
+ ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING,
"back",
&ANASTASIS_back_generic_decrement_
},
@@ -4674,7 +4880,7 @@ ANASTASIS_backup_action_ (json_t *state,
"back",
&back_finished
},
- { ANASTASIS_BACKUP_STATE_ERROR, NULL, NULL }
+ { ANASTASIS_BACKUP_STATE_INVALID, NULL, NULL }
};
const char *s = json_string_value (json_object_get (state,
"backup_state"));
@@ -4682,7 +4888,7 @@ ANASTASIS_backup_action_ (json_t *state,
GNUNET_assert (NULL != s); /* holds as per invariant of caller */
bs = ANASTASIS_backup_state_from_string_ (s);
- if (ANASTASIS_BACKUP_STATE_ERROR == bs)
+ if (ANASTASIS_BACKUP_STATE_INVALID == bs)
{
ANASTASIS_redux_fail_ (cb,
cb_cls,
@@ -4935,7 +5141,25 @@ ANASTASIS_REDUX_backup_begin_ (json_t *state,
json_object_foreach (provider_list, url, prov) {
struct BackupStartStateProviderEntry *pe;
json_t *istate;
+ const char *status;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_end ()
+ };
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (prov,
+ spec,
+ NULL, NULL))
+ {
+ /* skip malformed provider entry */
+ GNUNET_break_op (0);
+ continue;
+ }
+ if (0 == strcmp (status,
+ "disabled"))
+ continue;
pe = GNUNET_new (struct BackupStartStateProviderEntry);
pe->bss = bss;
istate = json_object ();
diff --git a/src/reducer/anastasis_api_discovery.c b/src/reducer/anastasis_api_discovery.c
new file mode 100644
index 0000000..3470d97
--- /dev/null
+++ b/src/reducer/anastasis_api_discovery.c
@@ -0,0 +1,549 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2022 Anastasis SARL
+
+ Anastasis 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.
+
+ Anastasis 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
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/anastasis_api_discovery.c
+ * @brief anastasis recovery policy discovery api
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <jansson.h>
+#include "anastasis_redux.h"
+#include "anastasis_error_codes.h"
+#include <taler/taler_json_lib.h>
+#include "anastasis_api_redux.h"
+#include <dlfcn.h>
+
+
+/**
+ * Handle for one request we are doing at a specific provider.
+ */
+struct ProviderOperation
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct ProviderOperation *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct ProviderOperation *prev;
+
+ /**
+ * Base URL of the provider.
+ */
+ char *provider_url;
+
+ /**
+ * Handle to the version check operation we are performing.
+ */
+ struct ANASTASIS_VersionCheck *vc;
+
+ /**
+ * Handle discovery operation we this is a part of.
+ */
+ struct ANASTASIS_PolicyDiscovery *pd;
+
+ /**
+ * Attribute mask applied to the identity attributes
+ * for this operation.
+ */
+ json_int_t attribute_mask;
+};
+
+
+/**
+ * Handle for a discovery operation.
+ */
+struct ANASTASIS_PolicyDiscovery
+{
+ /**
+ * Head of HTTP requests, kept in a DLL.
+ */
+ struct ProviderOperation *po_head;
+
+ /**
+ * Tail of HTTP requests, kept in a DLL.
+ */
+ struct ProviderOperation *po_tail;
+
+ /**
+ * Function to call with results.
+ */
+ ANASTASIS_PolicyDiscoveryCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Map for duplicate detection, maps hashes of policies we
+ * have already seen to a json_array with all providers
+ * and versions corresponding to this policy hash.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *dd_map;
+
+ /**
+ * State we are operating on.
+ */
+ json_t *state;
+
+ /**
+ * Number of optional fields in our identity attributes.
+ */
+ json_int_t opt_cnt;
+};
+
+
+/**
+ * Callback which passes back meta data about one of the
+ * recovery documents available at the provider.
+ *
+ * @param cls our `struct ProviderOperation *`
+ * @param version version number of the policy document,
+ * 0 for the end of the list
+ * @param server_time time of the backup at the provider
+ * @param recdoc_id hash of the compressed recovery document, uniquely
+ * identifies the document; NULL for the end of the list
+ * @param secret_name name of the secret as chosen by the user,
+ * or NULL if the user did not provide a name
+ */
+static void
+meta_cb (void *cls,
+ uint32_t version,
+ struct GNUNET_TIME_Timestamp server_time,
+ const struct GNUNET_HashCode *recdoc_id,
+ const char *secret_name)
+{
+ struct ProviderOperation *po = cls;
+ struct ANASTASIS_PolicyDiscovery *pd = po->pd;
+ json_t *pa;
+ json_t *pe;
+
+ if (NULL == recdoc_id)
+ {
+ po->vc = NULL;
+ GNUNET_CONTAINER_DLL_remove (pd->po_head,
+ pd->po_tail,
+ po);
+ GNUNET_free (po->provider_url);
+ GNUNET_free (po);
+ return;
+ }
+ pe = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("version",
+ version),
+ GNUNET_JSON_pack_string ("url",
+ po->provider_url));
+
+ pa = GNUNET_CONTAINER_multihashmap_get (pd->dd_map,
+ recdoc_id);
+ if (NULL != pa)
+ {
+ GNUNET_break (0 ==
+ json_array_append_new (pa,
+ pe));
+ return;
+ }
+ pa = json_array ();
+ GNUNET_assert (NULL != pa);
+ GNUNET_break (0 ==
+ json_array_append_new (pa,
+ pe));
+ GNUNET_assert (
+ GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ pd->dd_map,
+ recdoc_id,
+ pa,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ pd->cb (pd->cb_cls,
+ recdoc_id,
+ po->provider_url,
+ version,
+ po->attribute_mask,
+ server_time,
+ secret_name,
+ pa);
+}
+
+
+/**
+ * Start policy operation for @a pd using identity @a id_data
+ * at provider @a provider_url.
+ *
+ * @param pd policy discovery operation
+ * @param id_data our identity data, derived using @a mask
+ * @param mask the mask describing which optional attributes were removed
+ * @param provider_url which provider to query
+ * @param cursor cursor telling us from where to query
+ */
+static void
+start_po (struct ANASTASIS_PolicyDiscovery *pd,
+ const json_t *id_data,
+ json_int_t mask,
+ const char *provider_url,
+ const json_t *cursor)
+{
+ const json_t *state = pd->state;
+ struct ProviderOperation *po;
+ uint32_t max_version = UINT32_MAX;
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+
+ if (NULL != cursor)
+ {
+ size_t i;
+ json_t *obj;
+
+ json_array_foreach ((json_t *) cursor, i, obj)
+ {
+ const char *url;
+ uint64_t cmask;
+ uint32_t mv;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("provider_url",
+ &url),
+ GNUNET_JSON_spec_uint64 ("mask",
+ &cmask),
+ GNUNET_JSON_spec_uint32 ("max_version",
+ &mv),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (obj,
+ spec,
+ NULL, NULL))
+ {
+ /* cursor invalid */
+ GNUNET_break (0);
+ json_dumpf (obj,
+ stderr,
+ JSON_INDENT (2));
+ return;
+ }
+ if ( (cmask == mask) &&
+ (0 == strcmp (url,
+ provider_url)) )
+ {
+ max_version = mv;
+ break;
+ }
+ }
+ }
+
+ if (GNUNET_OK !=
+ ANASTASIS_reducer_lookup_salt (state,
+ provider_url,
+ &provider_salt))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No /config for `%s', skipping provider\n",
+ provider_url);
+ return;
+ }
+ po = GNUNET_new (struct ProviderOperation);
+ po->pd = pd;
+ po->attribute_mask = mask;
+ po->provider_url = GNUNET_strdup (provider_url);
+ po->vc = ANASTASIS_recovery_get_versions (ANASTASIS_REDUX_ctx_,
+ id_data,
+ max_version,
+ provider_url,
+ &provider_salt,
+ &meta_cb,
+ po);
+ if (NULL == po->vc)
+ {
+ GNUNET_free (po);
+ }
+ else
+ {
+ GNUNET_CONTAINER_DLL_insert (pd->po_head,
+ pd->po_tail,
+ po);
+ }
+}
+
+
+struct ANASTASIS_PolicyDiscovery *
+ANASTASIS_policy_discovery_start (const json_t *state,
+ const json_t *cursor,
+ ANASTASIS_PolicyDiscoveryCallback cb,
+ void *cb_cls)
+{
+ struct ANASTASIS_PolicyDiscovery *pd;
+ json_t *master_id = json_object_get (state,
+ "identity_attributes");
+ json_t *providers = json_object_get (state,
+ "authentication_providers");
+ json_t *required_attributes = json_object_get (state,
+ "required_attributes");
+ unsigned int opt_cnt;
+
+ if ( (NULL == master_id) ||
+ (! json_is_object (master_id)) )
+ {
+ GNUNET_break (0);
+ json_dumpf (state,
+ stderr,
+ JSON_INDENT (2));
+ return NULL;
+ }
+ if ( (NULL == providers) ||
+ (! json_is_object (providers)) )
+ {
+ GNUNET_break (0);
+ json_dumpf (state,
+ stderr,
+ JSON_INDENT (2));
+ return NULL;
+ }
+ if ( (NULL == required_attributes) ||
+ (! json_is_array (required_attributes)) )
+ {
+ GNUNET_break (0);
+ json_dumpf (required_attributes,
+ stderr,
+ JSON_INDENT (2));
+ return NULL;
+ }
+
+ /* count optional attributes present in 'master_id' */
+ opt_cnt = 0;
+ {
+ size_t index;
+ json_t *required_attribute;
+
+ json_array_foreach (required_attributes,
+ index,
+ required_attribute)
+ {
+ const char *name;
+ int optional = false;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("name",
+ &name),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_boolean ("optional",
+ &optional),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ bool present;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (required_attribute,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (required_attribute,
+ stderr,
+ JSON_INDENT (2));
+ return NULL;
+ }
+ present = (NULL !=
+ json_object_get (master_id,
+ name));
+ if ((! present) && (! optional))
+ {
+ GNUNET_break (0);
+ json_dumpf (master_id,
+ stderr,
+ JSON_INDENT (2));
+ return NULL;
+ }
+ if (present && optional)
+ opt_cnt++;
+ }
+ }
+
+ pd = GNUNET_new (struct ANASTASIS_PolicyDiscovery);
+ pd->dd_map = GNUNET_CONTAINER_multihashmap_create (128,
+ GNUNET_NO);
+ pd->cb = cb;
+ pd->cb_cls = cb_cls;
+ pd->opt_cnt = opt_cnt;
+ pd->state = json_deep_copy (state);
+
+ /* Compute 'id_data' for all possible masks, and then
+ start downloads at all providers for 'id_data' */
+ for (json_int_t mask = 0; mask < (1LL << opt_cnt); mask++)
+ {
+ json_t *id_data = ANASTASIS_mask_id_data (state,
+ master_id,
+ mask);
+ json_t *value;
+ const char *url;
+
+ json_object_foreach (providers, url, value)
+ {
+ start_po (pd,
+ id_data,
+ mask,
+ url,
+ cursor);
+ }
+ json_decref (id_data);
+ }
+ return pd;
+}
+
+
+void
+ANASTASIS_policy_discovery_more (struct ANASTASIS_PolicyDiscovery *pd,
+ const char *provider_url,
+ json_t *provider_state)
+{
+ json_t *master_id = json_object_get (pd->state,
+ "identity_attributes");
+ json_t *providers = json_object_get (pd->state,
+ "authentication_providers");
+
+ GNUNET_assert (NULL != master_id);
+ GNUNET_assert (NULL != providers);
+ GNUNET_assert (0 ==
+ json_object_set (providers,
+ provider_url,
+ provider_state));
+ /* Compute 'id_data' for all possible masks, and then
+ start downloads at provider for 'id_data' */
+ for (json_int_t mask = 0; mask < (1LL << pd->opt_cnt); mask++)
+ {
+ json_t *id_data = ANASTASIS_mask_id_data (pd->state,
+ master_id,
+ mask);
+
+ start_po (pd,
+ id_data,
+ mask,
+ provider_url,
+ NULL);
+ json_decref (id_data);
+ }
+}
+
+
+/**
+ * Free JSON Arrays from our hash map.
+ *
+ * @param cls NULL
+ * @param key ignored
+ * @param value `json_t *` to free
+ * @return #GNUNET_OK
+ */
+static enum GNUNET_GenericReturnValue
+free_dd_json (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ json_t *j = value;
+
+ (void) cls;
+ (void) key;
+ json_decref (j);
+ return GNUNET_OK;
+}
+
+
+void
+ANASTASIS_policy_discovery_stop (struct ANASTASIS_PolicyDiscovery *pd)
+{
+ struct ProviderOperation *po;
+
+ while (NULL != (po = pd->po_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (pd->po_head,
+ pd->po_tail,
+ po);
+ ANASTASIS_recovery_get_versions_cancel (po->vc);
+ GNUNET_free (po->provider_url);
+ GNUNET_free (po);
+ }
+ GNUNET_CONTAINER_multihashmap_iterate (pd->dd_map,
+ &free_dd_json,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (pd->dd_map);
+ json_decref (pd->state);
+ GNUNET_free (pd);
+}
+
+
+json_t *
+ANASTASIS_mask_id_data (const json_t *state,
+ const json_t *master_id,
+ json_int_t mask)
+{
+ json_t *required_attributes = json_object_get (state,
+ "required_attributes");
+ size_t index;
+ json_t *required_attribute;
+ json_t *ret = json_deep_copy (master_id);
+ unsigned int bit = 0;
+
+ if ( (NULL == required_attributes) ||
+ (! json_is_array (required_attributes)) )
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ json_array_foreach (required_attributes, index, required_attribute)
+ {
+ const char *name;
+ int optional = false;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("name",
+ &name),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_boolean ("optional",
+ &optional),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ bool present;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (required_attribute,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ present = (NULL !=
+ json_object_get (master_id,
+ name));
+ if ((! present) && (! optional))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (present && optional)
+ {
+ if (0 != ((1LL << bit) & mask))
+ {
+ GNUNET_assert (0 ==
+ json_object_del (ret,
+ name));
+ }
+ bit++;
+ }
+ }
+ return ret;
+}
diff --git a/src/reducer/anastasis_api_providers.c b/src/reducer/anastasis_api_providers.c
new file mode 100644
index 0000000..82243f5
--- /dev/null
+++ b/src/reducer/anastasis_api_providers.c
@@ -0,0 +1,300 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2020, 2021, 2022 Anastasis SARL
+
+ Anastasis 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.
+
+ Anastasis 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
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/anastasis_api_providers.c
+ * @brief anastasis provider synchronization logic
+ * @author Christian Grothoff
+ */
+
+#include <platform.h>
+#include <jansson.h>
+#include "anastasis_redux.h"
+#include "anastasis_error_codes.h"
+#include "anastasis_api_redux.h"
+
+
+/**
+ * Main data structure for sync_providers().
+ */
+struct MasterSync;
+
+
+/**
+ * Data structure for one provider we are syncing /config with.
+ */
+struct SyncEntry
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct SyncEntry *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct SyncEntry *prev;
+
+ /**
+ * Sync operation we are part of.
+ */
+ struct MasterSync *ms;
+
+ /**
+ * Redux action for this provider.
+ */
+ struct ANASTASIS_ReduxAction *ra;
+};
+
+
+/**
+ * Main data structure for sync_providers().
+ */
+struct MasterSync
+{
+ /**
+ * Our own sync action we expose externally.
+ */
+ struct ANASTASIS_ReduxAction ra;
+ /**
+ * Head of DLL with entries per provider.
+ */
+ struct SyncEntry *se_head;
+ /**
+ * Tail of DLL with entries per provider.
+ */
+ struct SyncEntry *se_tail;
+
+ /**
+ * Function to call with the result.
+ */
+ ANASTASIS_ActionCallback cb;
+
+ /**
+ * Closure for @e cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * Free @a cls data structure.
+ *
+ * @param[in] cls data structure to free, must be a `struct MasterSync *`
+ */
+static void
+clean_sync (void *cls)
+{
+ struct MasterSync *ms = cls;
+ struct SyncEntry *se;
+
+ while (NULL != (se = ms->se_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ms->se_head,
+ ms->se_tail,
+ se);
+ se->ra->cleanup (se->ra->cleanup_cls);
+ GNUNET_free (se);
+ }
+ GNUNET_free (ms);
+}
+
+
+/**
+ * Function called when we have made progress on any of the
+ * providers we are trying to sync with.
+ *
+ * @param cls closure
+ * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state
+ * @param new_state the new state of the operation (client should json_incref() to keep an alias)
+ */
+static void
+sync_progress (void *cls,
+ enum TALER_ErrorCode error,
+ json_t *new_state)
+{
+ struct SyncEntry *se = cls;
+ struct MasterSync *ms = se->ms;
+
+ GNUNET_CONTAINER_DLL_remove (ms->se_head,
+ ms->se_tail,
+ se);
+ GNUNET_free (se);
+ ms->cb (ms->cb_cls,
+ error,
+ new_state);
+ clean_sync (ms);
+}
+
+
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_sync_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ json_t *rd;
+ json_t *cs_arr;
+ struct MasterSync *ms;
+
+ rd = json_object_get (state,
+ "recovery_document");
+ if (NULL == rd)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' missing");
+ return NULL;
+ }
+ cs_arr = json_object_get (rd,
+ "challenges");
+ if (! json_is_array (cs_arr))
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' must be an array");
+ return NULL;
+ }
+ ms = GNUNET_new (struct MasterSync);
+ ms->cb = cb;
+ ms->cb_cls = cb_cls;
+ {
+ json_t *cs;
+ unsigned int n_index;
+
+ json_array_foreach (cs_arr, n_index, cs)
+ {
+ const char *provider_url;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("url",
+ &provider_url),
+ GNUNET_JSON_spec_end ()
+ };
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+ struct SyncEntry *se;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cs,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_document' missing");
+ clean_sync (ms);
+ return NULL;
+ }
+ if (GNUNET_OK ==
+ ANASTASIS_reducer_lookup_salt (state,
+ provider_url,
+ &provider_salt))
+ continue; /* provider already ready */
+ se = GNUNET_new (struct SyncEntry);
+ se->ms = ms;
+ GNUNET_CONTAINER_DLL_insert (ms->se_head,
+ ms->se_tail,
+ se);
+ se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
+ state,
+ &sync_progress,
+ se);
+ }
+ }
+ if (NULL == ms->se_head)
+ {
+ /* everything already synced */
+ clean_sync (ms);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED,
+ "already in sync");
+ return NULL;
+ }
+ ms->ra.cleanup = &clean_sync;
+ ms->ra.cleanup_cls = ms;
+ return &ms->ra;
+}
+
+
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_poll_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ json_t *ap;
+ const char *url;
+ json_t *obj;
+ struct MasterSync *ms;
+
+ ap = json_object_get (state,
+ "authentication_providers");
+ if (NULL == ap)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'authentication_providers' missing");
+ return NULL;
+ }
+ ms = GNUNET_new (struct MasterSync);
+ ms->cb = cb;
+ ms->cb_cls = cb_cls;
+ json_object_foreach (ap, url, obj)
+ {
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+ struct SyncEntry *se;
+ struct ANASTASIS_ReduxAction *ra;
+
+ if (GNUNET_OK ==
+ ANASTASIS_reducer_lookup_salt (state,
+ url,
+ &provider_salt))
+ continue;
+ se = GNUNET_new (struct SyncEntry);
+ se->ms = ms;
+ GNUNET_CONTAINER_DLL_insert (ms->se_head,
+ ms->se_tail,
+ se);
+ ra = ANASTASIS_REDUX_add_provider_to_state_ (url,
+ state,
+ &sync_progress,
+ se);
+ if (NULL == ra)
+ return NULL; /* sync_progress already called! */
+ se->ra = ra;
+ }
+ if (NULL == ms->se_head)
+ {
+ /* everything already synced */
+ clean_sync (ms);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
+ "already in sync");
+ return NULL;
+ }
+ ms->ra.cleanup = &clean_sync;
+ ms->ra.cleanup_cls = ms;
+ return &ms->ra;
+}
diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c
index 897a6dd..e795c55 100644
--- a/src/reducer/anastasis_api_recovery_redux.c
+++ b/src/reducer/anastasis_api_recovery_redux.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
@@ -44,7 +44,7 @@ ANASTASIS_recovery_state_from_string_ (const char *state_string)
if (0 == strcmp (state_string,
recovery_strings[i]))
return i;
- return ANASTASIS_RECOVERY_STATE_ERROR;
+ return ANASTASIS_RECOVERY_STATE_INVALID;
}
@@ -83,11 +83,79 @@ json_t *
ANASTASIS_recovery_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
json_t *initial_state;
+ const char *external_reducer = ANASTASIS_REDUX_probe_external_reducer ();
+
+ if (NULL != external_reducer)
+ {
+ int pipefd_stdout[2];
+ pid_t pid = 0;
+ int status;
+ FILE *reducer_stdout;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Using external reducer '%s' for recovery start status\n",
+ external_reducer);
+
+ GNUNET_assert (0 == pipe (pipefd_stdout));
+ pid = fork ();
+ if (pid == 0)
+ {
+ (void) close (pipefd_stdout[0]);
+ (void) dup2 (pipefd_stdout[1],
+ STDOUT_FILENO);
+ execlp (external_reducer,
+ external_reducer,
+ "-r",
+ NULL);
+ GNUNET_assert (0);
+ }
+
+ close (pipefd_stdout[1]);
+ reducer_stdout = fdopen (pipefd_stdout[0],
+ "r");
+ {
+ json_error_t err;
+
+ initial_state = json_loadf (reducer_stdout,
+ 0,
+ &err);
+
+ if (NULL == initial_state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "External reducer did not output valid JSON: %s:%d:%d %s\n",
+ err.source,
+ err.line,
+ err.column,
+ err.text);
+ GNUNET_assert (0 == fclose (reducer_stdout));
+ waitpid (pid, &status, 0);
+ return NULL;
+ }
+ }
+
+ GNUNET_assert (NULL != initial_state);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Waiting for external reducer to terminate.\n");
+ GNUNET_assert (0 == fclose (reducer_stdout));
+ reducer_stdout = NULL;
+ waitpid (pid, &status, 0);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "External reducer finished with exit status '%d'\n",
+ status);
+ return initial_state;
+ }
(void) cfg;
initial_state = ANASTASIS_REDUX_load_continents_ ();
if (NULL == initial_state)
return NULL;
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (initial_state,
+ "reducer_type",
+ json_string ("recovery")));
set_state (initial_state,
ANASTASIS_RECOVERY_STATE_CONTINENT_SELECTING);
return initial_state;
@@ -188,15 +256,16 @@ sctx_free (void *cls)
/**
- * Update @a state to reflect the error provided in @a rc.
+ * Call the action callback with an error result
*
- * @param[in,out] state state to update
+ * @param cb action callback to call
+ * @param cb_cls closure for @a cb
* @param rc error code to translate to JSON
- * @return error code to use
*/
-static enum TALER_ErrorCode
-update_state_by_error (json_t *state,
- enum ANASTASIS_RecoveryStatus rc)
+void
+fail_by_error (ANASTASIS_ActionCallback cb,
+ void *cb_cls,
+ enum ANASTASIS_RecoveryStatus rc)
{
const char *msg = NULL;
enum TALER_ErrorCode ec = TALER_EC_INVALID;
@@ -249,17 +318,10 @@ update_state_by_error (json_t *state,
ec = TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED;
break;
}
- GNUNET_assert (0 ==
- json_object_set_new (state,
- "error_message",
- json_string (msg)));
- GNUNET_assert (0 ==
- json_object_set_new (state,
- "error_code",
- json_integer (rc)));
- set_state (state,
- ANASTASIS_RECOVERY_STATE_ERROR);
- return ec;
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ ec,
+ msg);
}
@@ -279,7 +341,6 @@ core_secret_cb (void *cls,
size_t secret_size)
{
struct SelectChallengeContext *sctx = cls;
- enum TALER_ErrorCode ec;
sctx->r = NULL;
if (ANASTASIS_RS_SUCCESS == rc)
@@ -311,11 +372,9 @@ core_secret_cb (void *cls,
sctx_free (sctx);
return;
}
- ec = update_state_by_error (sctx->state,
- rc);
- sctx->cb (sctx->cb_cls,
- ec,
- sctx->state);
+ fail_by_error (sctx->cb,
+ sctx->cb_cls,
+ rc);
sctx_free (sctx);
}
@@ -399,7 +458,7 @@ find_challenge_in_ri (json_t *state,
/**
- * Find challenge of @a uuid in @a state under "cs".
+ * Find challenge of @a uuid in @a state under "challenges".
*
* @param state the state to search
* @param uuid the UUID to search for
@@ -412,7 +471,7 @@ find_challenge_in_cs (json_t *state,
json_t *rd = json_object_get (state,
"recovery_document");
json_t *cs = json_object_get (rd,
- "cs");
+ "challenges");
json_t *c;
size_t off;
@@ -451,7 +510,7 @@ find_challenge_in_cs (json_t *state,
* @param csr response details
*/
static void
-answer_feedback_cb (
+start_feedback_cb (
void *cls,
const struct ANASTASIS_ChallengeStartResponse *csr)
{
@@ -480,115 +539,50 @@ answer_feedback_cb (
}
switch (csr->cs)
{
- case ANASTASIS_CHALLENGE_STATUS_SOLVED:
+ case ANASTASIS_CHALLENGE_START_STATUS_FILENAME_PROVIDED:
{
- json_t *rd;
-
- rd = ANASTASIS_recovery_serialize (sctx->r);
- if (NULL == rd)
- {
- GNUNET_break (0);
- set_state (sctx->state,
- ANASTASIS_RECOVERY_STATE_ERROR);
- sctx->cb (sctx->cb_cls,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- sctx->state);
- sctx_free (sctx);
- return;
- }
- GNUNET_assert (0 ==
- json_object_set_new (sctx->state,
- "recovery_document",
- rd));
- }
- {
- json_t *solved;
+ json_t *instructions;
+ char *hint;
- solved = GNUNET_JSON_PACK (
+ GNUNET_asprintf (&hint,
+ _ ("Required TAN can be found in `%s'"),
+ csr->details.tan_filename);
+ instructions = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "solved"));
+ "code-in-file"),
+ GNUNET_JSON_pack_string ("filename",
+ csr->details.tan_filename),
+ GNUNET_JSON_pack_string ("display_hint",
+ hint));
+ GNUNET_free (hint);
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
- solved));
+ instructions));
}
- /* Delay reporting challenge success, as we MAY still
- also see a secret recovery success (and we can only
- call the callback once) */
- sctx->delayed_report = GNUNET_SCHEDULER_add_now (&report_solved,
- sctx);
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_INSTRUCTIONS:
+ case ANASTASIS_CHALLENGE_START_STATUS_TAN_SENT_HINT_PROVIDED:
{
json_t *instructions;
- const char *mime;
-
- mime = csr->details.open_challenge.content_type;
- if (NULL != mime)
- {
- if ( (0 == strcasecmp (mime,
- "text/plain")) ||
- (0 == strcasecmp (mime,
- "text/utf8")) )
- {
- char *s = GNUNET_strndup (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size);
-
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "hint"),
- GNUNET_JSON_pack_string ("hint",
- s),
- GNUNET_JSON_pack_uint64 ("http_status",
- (json_int_t) csr->details.open_challenge.
- http_status));
- GNUNET_free (s);
- }
- else if (0 == strcasecmp (mime,
- "application/json"))
- {
- json_t *body;
+ char *hint;
- body = json_loadb (csr->details.open_challenge.body,
- csr->details.open_challenge.body_size,
- JSON_REJECT_DUPLICATES,
- NULL);
- if (NULL == body)
- {
- GNUNET_break_op (0);
- mime = NULL;
- }
- else
- {
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "details"),
- GNUNET_JSON_pack_object_steal ("details",
- body),
- GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.open_challenge.http_status));
- }
- }
- else
- {
- /* unexpected / unsupported mime type */
- mime = NULL;
- }
- }
- if (NULL == mime)
- {
- instructions = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "body"),
- GNUNET_JSON_pack_data_varsize ("body",
- csr->details.open_challenge.body,
- csr->details.open_challenge.body_size),
- GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.open_challenge.http_status),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("mime_type",
- mime)));
- }
+ GNUNET_asprintf (&hint,
+ _ ("TAN code was sent to `%s'"),
+ csr->details.tan_address_hint);
+ instructions = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "send-to-address"),
+ GNUNET_JSON_pack_string ("address_hint",
+ csr->details.tan_address_hint),
+ GNUNET_JSON_pack_string ("display_hint",
+ hint));
+ GNUNET_free (hint);
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -601,20 +595,24 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_REDIRECT_FOR_AUTHENTICATION:
+
+ case ANASTASIS_CHALLENGE_START_STATUS_TAN_ALREADY_SENT:
{
- json_t *redir;
+ json_t *instructions;
+ char *hint;
- redir = GNUNET_JSON_PACK (
+ GNUNET_asprintf (&hint,
+ _ ("TAN code already sent."));
+ instructions = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "redirect"),
- GNUNET_JSON_pack_string ("redirect_url",
- csr->details.redirect_url));
- GNUNET_assert (NULL != redir);
+ "send-to-address"),
+ GNUNET_JSON_pack_string ("display_hint",
+ hint));
+ GNUNET_free (hint);
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
- redir));
+ instructions));
}
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
@@ -623,21 +621,29 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_PAYMENT_REQUIRED:
+
+ case ANASTASIS_CHALLENGE_START_STATUS_PAYMENT_REQUIRED:
{
json_t *pay;
+ char *hint;
+ GNUNET_asprintf (&hint,
+ _ ("Taler payment to `%s' required"),
+ csr->details.payment_required.taler_pay_uri);
pay = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "payment"),
- GNUNET_JSON_pack_string ("taler_pay_uri",
- csr->details.payment_required.
- taler_pay_uri),
+ "taler-payment"),
+ GNUNET_JSON_pack_string (
+ "taler_pay_uri",
+ csr->details.payment_required.taler_pay_uri),
GNUNET_JSON_pack_string ("provider",
cd->provider_url),
+ GNUNET_JSON_pack_string ("display_hint",
+ hint),
GNUNET_JSON_pack_data_auto (
"payment_secret",
&csr->details.payment_required.payment_secret));
+ GNUNET_free (hint);
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -663,7 +669,7 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_SERVER_FAILURE:
+ case ANASTASIS_CHALLENGE_START_STATUS_SERVER_FAILURE:
{
json_t *err;
@@ -671,10 +677,9 @@ answer_feedback_cb (
GNUNET_JSON_pack_string ("state",
"server-failure"),
GNUNET_JSON_pack_uint64 ("http_status",
- csr->details.server_failure.
- http_status),
+ csr->http_status),
GNUNET_JSON_pack_uint64 ("error_code",
- csr->details.server_failure.ec));
+ csr->ec));
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -683,17 +688,19 @@ answer_feedback_cb (
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
sctx->cb (sctx->cb_cls,
- csr->details.server_failure.ec,
+ csr->ec,
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_TRUTH_UNKNOWN:
+ case ANASTASIS_CHALLENGE_START_STATUS_TRUTH_UNKNOWN:
{
json_t *err;
err = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
"truth-unknown"),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status),
GNUNET_JSON_pack_uint64 ("error_code",
TALER_EC_ANASTASIS_TRUTH_UNKNOWN));
GNUNET_assert (0 ==
@@ -708,15 +715,242 @@ answer_feedback_cb (
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_RATE_LIMIT_EXCEEDED:
+ case ANASTASIS_CHALLENGE_START_STATUS_BANK_TRANSFER_REQUIRED:
+ {
+ json_t *reply;
+ json_t *c;
+ char *hint;
+
+ c = find_challenge_in_cs (sctx->state,
+ &cd->uuid);
+ if (NULL == c)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (c,
+ "async",
+ json_true ()));
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (
+ c,
+ "answer-pin",
+ json_integer (
+ csr->details.bank_transfer_required.answer_code)));
+ GNUNET_asprintf (&hint,
+ _ ("Wire %s to %s (%s) with subject %s\n"),
+ TALER_amount2s (
+ &csr->details.bank_transfer_required.amount),
+ csr->details.bank_transfer_required.target_iban,
+ csr->details.bank_transfer_required.target_business_name,
+ csr->details.bank_transfer_required.wire_transfer_subject);
+ reply = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "iban-instructions"),
+ GNUNET_JSON_pack_string (
+ "target_iban",
+ csr->details.bank_transfer_required.target_iban),
+ GNUNET_JSON_pack_string (
+ "display_hint",
+ hint),
+ GNUNET_JSON_pack_string (
+ "target_business_name",
+ csr->details.bank_transfer_required.target_business_name),
+ GNUNET_JSON_pack_string (
+ "wire_transfer_subject",
+ csr->details.bank_transfer_required.wire_transfer_subject),
+ TALER_JSON_pack_amount (
+ "challenge_amount",
+ &csr->details.bank_transfer_required.amount));
+ GNUNET_free (hint);
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ reply));
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (sctx->state,
+ "selected_challenge_uuid",
+ GNUNET_JSON_from_data_auto (
+ &cd->uuid)));
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ NULL);
+ sctx_free (sctx);
+}
+
+
+/**
+ * Defines a callback for the response status for a challenge answer
+ * operation.
+ *
+ * @param cls a `struct SelectChallengeContext *`
+ * @param csr response details
+ */
+static void
+answer_feedback_cb (
+ void *cls,
+ const struct ANASTASIS_ChallengeAnswerResponse *csr)
+{
+ struct SelectChallengeContext *sctx = cls;
+ const struct ANASTASIS_ChallengeDetails *cd;
+ char uuid[sizeof (cd->uuid) * 2];
+ char *end;
+ json_t *feedback;
+
+ cd = ANASTASIS_challenge_get_details (csr->challenge);
+ end = GNUNET_STRINGS_data_to_string (&cd->uuid,
+ sizeof (cd->uuid),
+ uuid,
+ sizeof (uuid));
+ GNUNET_assert (NULL != end);
+ *end = '\0';
+ feedback = json_object_get (sctx->state,
+ "challenge_feedback");
+ if (NULL == feedback)
+ {
+ feedback = json_object ();
+ GNUNET_assert (0 ==
+ json_object_set_new (sctx->state,
+ "challenge_feedback",
+ feedback));
+ }
+ switch (csr->cs)
+ {
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SOLVED:
+ {
+ json_t *rd;
+
+ rd = ANASTASIS_recovery_serialize (sctx->r);
+ if (NULL == rd)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (sctx->cb,
+ sctx->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unable to serialize recovery state");
+ sctx_free (sctx);
+ return;
+ }
+ GNUNET_assert (0 ==
+ json_object_set_new (sctx->state,
+ "recovery_document",
+ rd));
+ }
+ {
+ json_t *solved;
+
+ solved = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "solved"));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ solved));
+ }
+ /* Delay reporting challenge success, as we MAY still
+ also see a secret recovery success (and we can only
+ call the callback once) */
+ sctx->delayed_report = GNUNET_SCHEDULER_add_now (&report_solved,
+ sctx);
+ return;
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_INVALID_ANSWER:
+ {
+ json_t *instructions;
+
+ instructions = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "incorrect-answer"),
+ GNUNET_JSON_pack_uint64 ("error_code",
+ csr->ec));
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ instructions));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_PAYMENT_REQUIRED:
+ {
+ json_t *pay;
+ char *hint;
+
+ GNUNET_asprintf (&hint,
+ _ ("Taler payment to `%s' required"),
+ csr->details.payment_required.taler_pay_uri);
+ pay = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("state",
+ "taler-payment"),
+ GNUNET_JSON_pack_string (
+ "taler_pay_uri",
+ csr->details.payment_required.taler_pay_uri),
+ GNUNET_JSON_pack_string (
+ "display_hint",
+ hint),
+ GNUNET_JSON_pack_string ("provider",
+ cd->provider_url),
+ GNUNET_JSON_pack_data_auto (
+ "payment_secret",
+ &csr->details.payment_required.payment_secret));
+ GNUNET_free (hint);
+ GNUNET_assert (0 ==
+ json_object_set_new (feedback,
+ uuid,
+ pay));
+ }
+ /* Remember payment secret for later (once application claims it paid) */
+ {
+ json_t *challenge = find_challenge_in_ri (sctx->state,
+ &cd->uuid);
+
+ GNUNET_assert (NULL != challenge);
+ GNUNET_assert (0 ==
+ json_object_set_new (
+ challenge,
+ "payment_secret",
+ GNUNET_JSON_from_data_auto (
+ &csr->details.payment_required.payment_secret)));
+ }
+ set_state (sctx->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING);
+ sctx->cb (sctx->cb_cls,
+ TALER_EC_NONE,
+ sctx->state);
+ sctx_free (sctx);
+ return;
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_SERVER_FAILURE:
{
json_t *err;
err = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "rate-limit-exceeded"),
+ "server-failure"),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status),
GNUNET_JSON_pack_uint64 ("error_code",
- TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED));
+ csr->ec));
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
@@ -725,119 +959,71 @@ answer_feedback_cb (
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
sctx->cb (sctx->cb_cls,
- TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
+ csr->ec,
sctx->state);
sctx_free (sctx);
return;
- case ANASTASIS_CHALLENGE_STATUS_AUTH_TIMEOUT:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_TRUTH_UNKNOWN:
{
json_t *err;
err = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("state",
- "authentication-timeout"),
+ "truth-unknown"),
+ GNUNET_JSON_pack_uint64 ("http_status",
+ csr->http_status),
GNUNET_JSON_pack_uint64 ("error_code",
- TALER_EC_ANASTASIS_TRUTH_AUTH_TIMEOUT));
+ TALER_EC_ANASTASIS_TRUTH_UNKNOWN));
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
err));
}
- GNUNET_break_op (0);
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
sctx->cb (sctx->cb_cls,
- TALER_EC_ANASTASIS_TRUTH_AUTH_TIMEOUT,
+ TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
sctx->state);
sctx_free (sctx);
return;
-
- case ANASTASIS_CHALLENGE_STATUS_EXTERNAL_INSTRUCTIONS:
+ case ANASTASIS_CHALLENGE_ANSWER_STATUS_RATE_LIMIT_EXCEEDED:
{
- const json_t *body = csr->details.external_challenge;
- const char *method;
- json_t *details;
- bool is_async = false;
- uint64_t code = 0;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("method",
- &method),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("async",
- &is_async)),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint64 ("answer_code",
- &code)),
- GNUNET_JSON_spec_json ("details",
- &details),
- GNUNET_JSON_spec_end ()
- };
- json_t *reply;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (body,
- spec,
- NULL, NULL))
- {
- json_t *err;
-
- GNUNET_break_op (0);
- err = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "server-failure"),
- GNUNET_JSON_pack_uint64 ("error_code",
- TALER_EC_GENERIC_REPLY_MALFORMED));
- GNUNET_assert (0 ==
- json_object_set_new (feedback,
- uuid,
- err));
- return;
- }
- if (is_async)
- {
- json_t *c = find_challenge_in_cs (sctx->state,
- &cd->uuid);
-
- if (NULL == c)
- {
- GNUNET_break (0);
- set_state (sctx->state,
- ANASTASIS_RECOVERY_STATE_ERROR);
- sctx->cb (sctx->cb_cls,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- sctx->state);
- sctx_free (sctx);
- return;
- }
- GNUNET_assert (0 ==
- json_object_set_new (c,
- "async",
- json_true ()));
- GNUNET_assert (0 ==
- json_object_set_new (c,
- "answer-pin",
- json_integer (code)));
- }
- reply = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("state",
- "external-instructions"),
- GNUNET_JSON_pack_string ("method",
- method),
- GNUNET_JSON_pack_object_incref ("details",
- details));
- GNUNET_JSON_parse_free (spec);
+ json_t *err;
+ char *hint;
+
+ GNUNET_asprintf (
+ &hint,
+ _ ("exceeded limit of %llu attempts in %s"),
+ (unsigned long long) csr->details.rate_limit_exceeded.request_limit,
+ GNUNET_TIME_relative2s (
+ csr->details.rate_limit_exceeded.request_frequency,
+ true));
+ err = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "state",
+ "rate-limit-exceeded"),
+ GNUNET_JSON_pack_string (
+ "display_hint",
+ hint),
+ GNUNET_JSON_pack_uint64 (
+ "request_limit",
+ csr->details.rate_limit_exceeded.request_limit),
+ GNUNET_JSON_pack_time_rel (
+ "request_frequency",
+ csr->details.rate_limit_exceeded.request_frequency),
+ GNUNET_JSON_pack_uint64 (
+ "error_code",
+ TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED));
+ GNUNET_free (hint);
GNUNET_assert (0 ==
json_object_set_new (feedback,
uuid,
- reply));
+ err));
}
- json_object_set_new (sctx->state,
- "selected_challenge_uuid",
- GNUNET_JSON_from_data_auto (&cd->uuid));
set_state (sctx->state,
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
sctx->cb (sctx->cb_cls,
- TALER_EC_NONE,
+ TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED,
sctx->state);
sctx_free (sctx);
return;
@@ -873,7 +1059,8 @@ solve_challenge_cb (void *cls,
struct GNUNET_JSON_Specification tspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("timeout",
- &timeout)),
+ &timeout),
+ NULL),
GNUNET_JSON_spec_end ()
};
struct GNUNET_JSON_Specification pspec[] = {
@@ -1138,21 +1325,19 @@ solve_challenge_cb (void *cls,
sctx_free (sctx);
return;
}
- ret = ANASTASIS_challenge_start (ci,
- psp,
- timeout,
- &hashed_answer,
- &answer_feedback_cb,
- sctx);
+ ret = ANASTASIS_challenge_answer3 (ci,
+ psp,
+ timeout,
+ &hashed_answer,
+ &answer_feedback_cb,
+ sctx);
}
else
{
/* no answer provided */
ret = ANASTASIS_challenge_start (ci,
psp,
- timeout,
- NULL, /* no answer */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
}
@@ -1272,9 +1457,7 @@ pay_challenge_cb (void *cls,
{
ret = ANASTASIS_challenge_start (ci,
&sctx->ps,
- sctx->timeout,
- NULL, /* no answer yet */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
if (GNUNET_OK != ret)
@@ -1468,7 +1651,8 @@ pay_challenge (json_t *state,
struct GNUNET_JSON_Specification aspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("timeout",
- &timeout)),
+ &timeout),
+ NULL),
GNUNET_JSON_spec_fixed_auto ("payment_secret",
&sctx->ps),
GNUNET_JSON_spec_end ()
@@ -1566,7 +1750,8 @@ select_challenge_cb (void *cls,
struct GNUNET_JSON_Specification tspec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_relative_time ("timeout",
- &timeout)),
+ &timeout),
+ NULL),
GNUNET_JSON_spec_end ()
};
struct GNUNET_JSON_Specification pspec[] = {
@@ -1684,10 +1869,13 @@ select_challenge_cb (void *cls,
json_object_set_new (sctx->state,
"selected_challenge_uuid",
GNUNET_JSON_from_data_auto (&cd->uuid)));
- if (0 == strcmp ("question",
- cd->type))
+ if ( (0 == strcmp ("question",
+ cd->type)) ||
+ (0 == strcmp ("totp",
+ cd->type)) )
{
- /* security question, immediately request user to answer it */
+ /* security question or TOTP:
+ immediately request user to answer it */
set_state (sctx->state,
ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING);
sctx->cb (sctx->cb_cls,
@@ -1718,9 +1906,7 @@ select_challenge_cb (void *cls,
{
ret = ANASTASIS_challenge_start (ci,
psp,
- timeout,
- NULL, /* no answer */
- &answer_feedback_cb,
+ &start_feedback_cb,
sctx);
}
}
@@ -1856,256 +2042,15 @@ back_challenge_solving (json_t *state,
/**
- * The user wants us to change the policy version. Download another version.
- *
- * @param[in] state we are in
- * @param arguments our arguments with the solution
- * @param cb functiont o call with the new state
- * @param cb_cls closure for @a cb
- * @return handle to cancel challenge selection step
- */
-static struct ANASTASIS_ReduxAction *
-change_version (json_t *state,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls)
-{
- uint64_t version;
- const char *provider_url;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_uint64 ("version",
- &version),
- GNUNET_JSON_spec_string ("provider_url",
- &provider_url),
- GNUNET_JSON_spec_end ()
- };
- json_t *ia;
- json_t *args;
- struct ANASTASIS_ReduxAction *ra;
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (arguments,
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "'version' invalid");
- return NULL;
- }
- GNUNET_assert (NULL != provider_url);
- ia = json_object_get (state,
- "identity_attributes");
- if (NULL == ia)
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "'identity_attributes' missing");
- return NULL;
- }
- args = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("version",
- version),
- GNUNET_JSON_pack_object_incref ("identity_attributes",
- (json_t *) ia),
- GNUNET_JSON_pack_string ("provider_url",
- provider_url));
- if (NULL == args)
- {
- GNUNET_break (0);
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- NULL);
- return NULL;
- }
- ra = ANASTASIS_REDUX_recovery_challenge_begin_ (state,
- args,
- cb,
- cb_cls);
- json_decref (args);
- return ra;
-}
-
-
-/**
- * DispatchHandler/Callback function which is called for a
- * "next" action in "secret_selecting" state.
- *
- * @param state state to operate on
- * @param arguments arguments to use for operation on state
- * @param cb callback to call during/after operation
- * @param cb_cls callback closure
- * @return NULL
- */
-static struct ANASTASIS_ReduxAction *
-done_secret_selecting (json_t *state,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls)
-{
- const json_t *ri;
-
- ri = json_object_get (state,
- "recovery_information");
- if ( (NULL == ri) ||
- (NULL == json_object_get (ri,
- "challenges")) )
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE,
- "no valid version selected");
- return NULL;
- }
- set_state (state,
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
- cb (cb_cls,
- TALER_EC_NONE,
- state);
- return NULL;
-}
-
-
-/**
- * Signature of callback function that implements a state transition.
- *
- * @param state current state
- * @param arguments arguments for the state transition
- * @param cb function to call when done
- * @param cb_cls closure for @a cb
- */
-typedef struct ANASTASIS_ReduxAction *
-(*DispatchHandler)(json_t *state,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls);
-
-
-struct ANASTASIS_ReduxAction *
-ANASTASIS_recovery_action_ (json_t *state,
- const char *action,
- const json_t *arguments,
- ANASTASIS_ActionCallback cb,
- void *cb_cls)
-{
- struct Dispatcher
- {
- enum ANASTASIS_RecoveryState recovery_state;
- const char *recovery_action;
- DispatchHandler fun;
- } dispatchers[] = {
- {
- ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
- "change_version",
- &change_version
- },
- {
- ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
- "next",
- &done_secret_selecting
- },
- {
- ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
- "back",
- &ANASTASIS_back_generic_decrement_
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
- "select_challenge",
- &select_challenge
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
- "poll",
- &poll_challenges
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
- "back",
- &ANASTASIS_back_generic_decrement_
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
- "pay",
- &pay_challenge
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
- "back",
- &ANASTASIS_back_generic_decrement_
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING,
- "solve_challenge",
- &solve_challenge
- },
- {
- ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING,
- "back",
- &back_challenge_solving
- },
- { ANASTASIS_RECOVERY_STATE_ERROR, NULL, NULL }
- };
- const char *s = json_string_value (json_object_get (state,
- "recovery_state"));
- enum ANASTASIS_RecoveryState rs;
-
- GNUNET_assert (NULL != s);
- rs = ANASTASIS_recovery_state_from_string_ (s);
- if (ANASTASIS_RECOVERY_STATE_ERROR == rs)
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'recovery_state' field invalid");
- return NULL;
- }
- for (unsigned int i = 0; NULL != dispatchers[i].fun; i++)
- {
- if ( (rs == dispatchers[i].recovery_state) &&
- (0 == strcmp (action,
- dispatchers[i].recovery_action)) )
- {
- return dispatchers[i].fun (state,
- arguments,
- cb,
- cb_cls);
- }
- }
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
- action);
- return NULL;
-}
-
-
-/**
- * State for a "recover secret" CMD.
- */
-struct RecoverSecretState;
-
-
-/**
* State for a "policy download" as part of a recovery operation.
*/
struct PolicyDownloadEntry
{
/**
- * Kept in a DLL.
- */
- struct PolicyDownloadEntry *prev;
-
- /**
- * Kept in a DLL.
+ * Redux action handle associated with this state.
*/
- struct PolicyDownloadEntry *next;
+ struct ANASTASIS_ReduxAction ra;
/**
* Backend we are querying.
@@ -2113,289 +2058,46 @@ struct PolicyDownloadEntry
char *backend_url;
/**
- * Salt to be used to derive the id for this provider
- */
- struct ANASTASIS_CRYPTO_ProviderSaltP salt;
-
- /**
- * Context we operate in.
- */
- struct RecoverSecretState *rss;
-
- /**
* The /policy GET operation handle.
*/
struct ANASTASIS_Recovery *recovery;
-};
-
-
-/**
- * Entry in the list of all known applicable Anastasis providers.
- * Used to wait for it to complete downloading /config.
- */
-struct RecoveryStartStateProviderEntry
-{
- /**
- * Kept in a DLL.
- */
- struct RecoveryStartStateProviderEntry *next;
-
- /**
- * Kept in a DLL.
- */
- struct RecoveryStartStateProviderEntry *prev;
-
- /**
- * Main operation this entry is part of.
- */
- struct RecoverSecretState *rss;
-
/**
- * Resulting provider information, NULL if not (yet) available.
- */
- json_t *istate;
-
- /**
- * Ongoing reducer action to obtain /config, NULL if completed.
- */
- struct ANASTASIS_ReduxAction *ra;
-
- /**
- * Final result of the operation (once completed).
- */
- enum TALER_ErrorCode ec;
-};
-
-
-/**
- * State for a "recover secret" CMD.
- */
-struct RecoverSecretState
-{
-
- /**
- * Redux action handle associated with this state.
- */
- struct ANASTASIS_ReduxAction ra;
-
- /**
- * Head of list of provider /config operations we are doing.
- */
- struct RecoveryStartStateProviderEntry *pe_head;
-
- /**
- * Tail of list of provider /config operations we are doing.
- */
- struct RecoveryStartStateProviderEntry *pe_tail;
-
- /**
- * Identification data from the user
- */
- json_t *id_data;
-
- /**
- * Head of DLL of policy downloads.
- */
- struct PolicyDownloadEntry *pd_head;
-
- /**
- * Tail of DLL of policy downloads.
- */
- struct PolicyDownloadEntry *pd_tail;
-
- /**
- * Reference to our state.
- */
- json_t *state;
-
- /**
- * callback to call during/after operation
+ * Function to call with the result.
*/
ANASTASIS_ActionCallback cb;
/**
- * closure for action callback @e cb.
+ * Closure for @e cb.
*/
void *cb_cls;
/**
- * Set if recovery must be done with this provider.
- */
- char *provider_url;
-
- /**
- * version of the recovery document to request.
- */
- unsigned int version;
-
- /**
- * Number of provider /config operations in @e ba_head that
- * are still awaiting completion.
+ * State we are using.
*/
- unsigned int pending;
+ json_t *state;
- /**
- * Is @e version set?
- */
- bool have_version;
};
/**
- * Function to free a `struct RecoverSecretState`
+ * Free @a cls data structure.
*
- * @param cls must be a `struct RecoverSecretState`
+ * @param[in] cls data structure to free, must be a `struct PolicyDownloadEntry *`
*/
static void
-free_rss (void *cls)
+free_pd (void *cls)
{
- struct RecoverSecretState *rss = cls;
- struct PolicyDownloadEntry *pd;
- struct RecoveryStartStateProviderEntry *pe;
+ struct PolicyDownloadEntry *pd = cls;
- while (NULL != (pe = rss->pe_head))
- {
- GNUNET_CONTAINER_DLL_remove (rss->pe_head,
- rss->pe_tail,
- pe);
- ANASTASIS_redux_action_cancel (pe->ra);
- rss->pending--;
- GNUNET_free (pe);
- }
- while (NULL != (pd = rss->pd_head))
+ if (NULL != pd->recovery)
{
- GNUNET_CONTAINER_DLL_remove (rss->pd_head,
- rss->pd_tail,
- pd);
- if (NULL != pd->recovery)
- {
- ANASTASIS_recovery_abort (pd->recovery);
- pd->recovery = NULL;
- }
- GNUNET_free (pd->backend_url);
- GNUNET_free (pd);
+ ANASTASIS_recovery_abort (pd->recovery);
+ pd->recovery = NULL;
}
- json_decref (rss->state);
- json_decref (rss->id_data);
- GNUNET_assert (0 == rss->pending);
- GNUNET_free (rss->provider_url);
- GNUNET_free (rss);
-}
-
-
-/**
- * This function is called whenever the recovery process ends.
- * In this case, that should not be possible as this callback
- * is used before we even begin with the challenges. So if
- * we are called, it is because of some fatal error.
- *
- * @param cls a `struct PolicyDownloadEntry`
- * @param rc error code
- * @param secret contains the core secret which is passed to the user
- * @param secret_size defines the size of the core secret
- */
-static void
-core_early_secret_cb (void *cls,
- enum ANASTASIS_RecoveryStatus rc,
- const void *secret,
- size_t secret_size)
-{
- struct PolicyDownloadEntry *pd = cls;
- struct RecoverSecretState *rss = pd->rss;
- enum TALER_ErrorCode ec;
-
- pd->recovery = NULL;
- GNUNET_assert (NULL == secret);
- GNUNET_CONTAINER_DLL_remove (rss->pd_head,
- rss->pd_tail,
- pd);
GNUNET_free (pd->backend_url);
+ json_decref (pd->state);
GNUNET_free (pd);
- if (NULL != rss->pd_head)
- return; /* wait for another one */
- /* all failed! report failure! */
- GNUNET_assert (ANASTASIS_RS_SUCCESS != rc);
- ec = update_state_by_error (rss->state,
- rc);
- rss->cb (rss->cb_cls,
- ec,
- rss->state);
- rss->cb = NULL;
- free_rss (rss);
-}
-
-
-/**
- * Determine recovery @a cost of solving a challenge of type @a type
- * at @a provider_url by inspecting @a state.
- *
- * @param state the state to inspect
- * @param provider_url the provider to lookup config info from
- * @param type the method to lookup the cost of
- * @param[out] cost the recovery cost to return
- * @return #GNUNET_OK on success, #GNUNET_NO if not found, #GNUNET_SYSERR on state error
- */
-static int
-lookup_cost (const json_t *state,
- const char *provider_url,
- const char *type,
- struct TALER_Amount *cost)
-{
- const json_t *providers;
- const json_t *provider;
- const json_t *methods;
-
- providers = json_object_get (state,
- "authentication_providers");
- if (NULL == providers)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- provider = json_object_get (providers,
- provider_url);
- if (NULL == provider)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- methods = json_object_get (provider,
- "methods");
- if ( (NULL == methods) ||
- (! json_is_array (methods)) )
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- {
- size_t index;
- json_t *method;
-
- json_array_foreach (methods, index, method) {
- const char *t;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("type",
- &t),
- TALER_JSON_spec_amount_any ("usage_fee",
- cost),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (method,
- spec,
- NULL, NULL))
- {
- GNUNET_break (0);
- continue;
- }
- if (0 == strcmp (t,
- type))
- return GNUNET_OK;
- }
- }
- return GNUNET_NO; /* not found */
}
@@ -2404,40 +2106,34 @@ lookup_cost (const json_t *state,
* allow the user to specify alternative providers and/or policy
* versions.
*
- * @param[in] rss state to fail with the policy download
+ * @param[in] pd state to fail with the policy download
* @param offline true of the reason to show is that all providers
* were offline / did not return a salt to us
*/
static void
-return_no_policy (struct RecoverSecretState *rss,
+return_no_policy (struct PolicyDownloadEntry *pd,
bool offline)
{
- json_t *msg;
+ enum TALER_ErrorCode ec = TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED;
+ const char *detail = (offline)
+ ? "could not contact provider (offline)"
+ : "provider does not know this policy";
+ json_t *estate;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "No provider online, need user to manually specify providers!\n");
- msg = GNUNET_JSON_PACK (
+ "Provider offline!\n");
+ estate = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("detail",
+ detail)),
+ GNUNET_JSON_pack_uint64 ("code",
+ ec),
GNUNET_JSON_pack_string ("hint",
- offline
- ? "could not contact provider"
- : "provider does not know you"),
- GNUNET_JSON_pack_bool ("offline",
- offline));
- GNUNET_assert (0 ==
- json_object_set_new (rss->state,
- "recovery_error",
- msg));
- /* In case there are old ones, remove them! */
- (void) json_object_del (rss->state,
- "recovery_document");
- (void) json_object_del (rss->state,
- "recovery_information");
- set_state (rss->state,
- ANASTASIS_RECOVERY_STATE_SECRET_SELECTING);
- rss->cb (rss->cb_cls,
- TALER_EC_NONE,
- rss->state);
- free_rss (rss);
+ TALER_ErrorCode_get_hint (ec)));
+ pd->cb (pd->cb_cls,
+ ec,
+ estate);
+ free_pd (pd);
}
@@ -2450,7 +2146,7 @@ return_no_policy (struct RecoverSecretState *rss,
* cancel all of the others, passing the obtained recovery information
* back to the user.
*
- * @param cls closure for the callback
+ * @param cls closure for the callback with a `struct PolicyDownloadEntry *`
* @param ri recovery information struct which contains the policies
*/
static void
@@ -2458,7 +2154,6 @@ policy_lookup_cb (void *cls,
const struct ANASTASIS_RecoveryInformation *ri)
{
struct PolicyDownloadEntry *pd = cls;
- struct RecoverSecretState *rss = pd->rss;
json_t *policies;
json_t *challenges;
json_t *recovery_information;
@@ -2466,16 +2161,10 @@ policy_lookup_cb (void *cls,
if (NULL == ri)
{
/* Woopsie, failed hard. */
- GNUNET_CONTAINER_DLL_remove (rss->pd_head,
- rss->pd_tail,
- pd);
ANASTASIS_recovery_abort (pd->recovery);
GNUNET_free (pd->backend_url);
GNUNET_free (pd);
- if (NULL != rss->pd_head)
- return; /* wait for another one */
- /* all failed! report failure! */
- return_no_policy (rss,
+ return_no_policy (pd,
false);
return;
}
@@ -2514,37 +2203,15 @@ policy_lookup_cb (void *cls,
struct ANASTASIS_Challenge *c = ri->cs[i];
const struct ANASTASIS_ChallengeDetails *cd;
json_t *cj;
- struct TALER_Amount cost;
- int ret;
cd = ANASTASIS_challenge_get_details (c);
- ret = lookup_cost (rss->state,
- cd->provider_url,
- cd->type,
- &cost);
- if (GNUNET_SYSERR == ret)
- {
- json_decref (challenges);
- json_decref (policies);
- set_state (rss->state,
- ANASTASIS_RECOVERY_STATE_ERROR);
- ANASTASIS_redux_fail_ (rss->cb,
- rss->cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "failed to 'lookup_cost'");
- free_rss (rss);
- return;
- }
-
cj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("uuid",
&cd->uuid),
- TALER_JSON_pack_amount ("cost",
- (GNUNET_NO == ret)
- ? NULL
- : &cost),
GNUNET_JSON_pack_string ("type",
cd->type),
+ GNUNET_JSON_pack_string ("uuid-display",
+ ANASTASIS_CRYPTO_uuid2s (&cd->uuid)),
GNUNET_JSON_pack_string ("instructions",
cd->instructions));
GNUNET_assert (0 ==
@@ -2564,7 +2231,7 @@ policy_lookup_cb (void *cls,
GNUNET_JSON_pack_uint64 ("version",
ri->version));
GNUNET_assert (0 ==
- json_object_set_new (rss->state,
+ json_object_set_new (pd->state,
"recovery_information",
recovery_information));
{
@@ -2574,229 +2241,387 @@ policy_lookup_cb (void *cls,
if (NULL == rd)
{
GNUNET_break (0);
- set_state (rss->state,
- ANASTASIS_RECOVERY_STATE_ERROR);
- rss->cb (rss->cb_cls,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- rss->state);
- free_rss (rss);
+ ANASTASIS_redux_fail_ (pd->cb,
+ pd->cb_cls,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unable to serialize recovery state");
+ free_pd (pd);
return;
}
GNUNET_assert (0 ==
- json_object_set_new (rss->state,
+ json_object_set_new (pd->state,
"recovery_document",
rd));
}
- /* In case there is an old error remove it! */
- (void) json_object_del (rss->state,
- "recovery_error");
- set_state (rss->state,
- ANASTASIS_RECOVERY_STATE_SECRET_SELECTING);
- rss->cb (rss->cb_cls,
- TALER_EC_NONE,
- rss->state);
- free_rss (rss);
+ set_state (pd->state,
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING);
+ pd->cb (pd->cb_cls,
+ TALER_EC_NONE,
+ pd->state);
+ free_pd (pd);
}
/**
- * Try to launch recovery at provider @a provider_url with config @a p_cfg.
+ * This function is called whenever the recovery process ends.
+ * In this case, that should not be possible as this callback
+ * is used before we even begin with the challenges. So if
+ * we are called, it is because of some fatal error.
*
- * @param[in,out] rss recovery context
- * @param provider_url base URL of the provider to try
- * @param p_cfg configuration of the provider
- * @return true if a recovery was launched
+ * @param cls a `struct PolicyDownloadEntry`
+ * @param rc error code
+ * @param secret contains the core secret which is passed to the user
+ * @param secret_size defines the size of the core secret
*/
-static bool
-launch_recovery (struct RecoverSecretState *rss,
- const char *provider_url,
- const json_t *p_cfg)
+static void
+core_early_secret_cb (void *cls,
+ enum ANASTASIS_RecoveryStatus rc,
+ const void *secret,
+ size_t secret_size)
{
- struct PolicyDownloadEntry *pd = GNUNET_new (struct PolicyDownloadEntry);
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("salt",
- &pd->salt),
- GNUNET_JSON_spec_end ()
- };
+ struct PolicyDownloadEntry *pd = cls;
- if (MHD_HTTP_OK !=
- json_integer_value (json_object_get (p_cfg,
- "http_status")))
- return false; /* skip providers that are down */
- if (GNUNET_OK !=
- GNUNET_JSON_parse (p_cfg,
- spec,
- NULL, NULL))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "No salt for `%s', provider offline?\n",
- provider_url);
- GNUNET_free (pd);
- return false;
- }
- pd->backend_url = GNUNET_strdup (provider_url);
- pd->rss = rss;
- pd->recovery = ANASTASIS_recovery_begin (ANASTASIS_REDUX_ctx_,
- rss->id_data,
- rss->have_version
- ? rss->version
- : 0,
- pd->backend_url,
- &pd->salt,
- &policy_lookup_cb,
- pd,
- &core_early_secret_cb,
- pd);
- if (NULL != pd->recovery)
- {
- GNUNET_CONTAINER_DLL_insert (rss->pd_head,
- rss->pd_tail,
- pd);
- return true;
- }
- GNUNET_free (pd->backend_url);
- GNUNET_free (pd);
- return false;
+ pd->recovery = NULL;
+ GNUNET_assert (NULL == secret);
+ GNUNET_assert (ANASTASIS_RS_SUCCESS != rc);
+ fail_by_error (pd->cb,
+ pd->cb_cls,
+ rc);
+ free_pd (pd);
}
/**
- * We finished downloading /config from all providers, merge
- * into the main state, trigger the continuation and free our
- * state.
+ * DispatchHandler/Callback function which is called for a
+ * "next" action in "secret_selecting" state.
*
- * @param[in] rss main state to merge into
+ * @param state state to operate on
+ * @param arguments arguments to use for operation on state
+ * @param cb callback to call during/after operation
+ * @param cb_cls callback closure
+ * @return NULL
*/
-static void
-providers_complete (struct RecoverSecretState *rss)
+static struct ANASTASIS_ReduxAction *
+done_secret_selecting (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
{
- bool launched = false;
- struct RecoveryStartStateProviderEntry *pe;
- json_t *tlist;
+ uint32_t mask;
+ const json_t *pa;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint32 ("attribute_mask",
+ &mask),
+ GNUNET_JSON_spec_array_const ("providers",
+ &pa),
+ GNUNET_JSON_spec_end ()
+ };
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
+ struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_fixed_auto ("provider_salt",
+ &provider_salt),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *p_cfg;
+ json_t *id_data;
+ const json_t *providers;
- tlist = json_object_get (rss->state,
- "authentication_providers");
- if (NULL == tlist)
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (arguments,
+ spec,
+ NULL, NULL))
{
- tlist = json_object ();
- GNUNET_assert (NULL != tlist);
- GNUNET_assert (0 ==
- json_object_set_new (rss->state,
- "authentication_providers",
- tlist));
+ GNUNET_break (0);
+ json_dumpf (arguments,
+ stderr,
+ JSON_INDENT (2));
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ NULL);
+ return NULL;
}
- while (NULL != (pe = rss->pe_head))
+ providers = json_object_get (state,
+ "authentication_providers");
+ if ( (NULL == providers) ||
+ (! json_is_object (providers)) )
{
- json_t *provider_list;
-
- GNUNET_CONTAINER_DLL_remove (rss->pe_head,
- rss->pe_tail,
- pe);
- provider_list = json_object_get (pe->istate,
- "authentication_providers");
- /* merge provider_list into tlist (overriding existing entries) */
- if (NULL != provider_list)
- {
- const char *url;
- json_t *value;
-
- json_object_foreach (provider_list, url, value) {
- GNUNET_assert (0 ==
- json_object_set (tlist,
- url,
- value));
- }
- }
- json_decref (pe->istate);
- GNUNET_free (pe);
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'authentication_providers' missing");
+ return NULL;
}
- /* now iterate over providers and begin downloading */
- if (NULL != rss->provider_url)
{
- json_t *p_cfg;
-
- p_cfg = json_object_get (tlist,
- rss->provider_url);
- if (NULL != p_cfg)
- launched = launch_recovery (rss,
- rss->provider_url,
- p_cfg);
- }
- else
- {
- json_t *p_cfg;
+ size_t poff;
+ json_t *pe;
+ uint64_t version;
const char *provider_url;
- json_object_foreach (tlist, provider_url, p_cfg)
+ json_array_foreach (pa, poff, pe)
{
- launched |= launch_recovery (rss,
- provider_url,
- p_cfg);
+ struct GNUNET_JSON_Specification ispec[] = {
+ GNUNET_JSON_spec_uint64 ("version",
+ &version),
+ GNUNET_JSON_spec_string ("url",
+ &provider_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (pe,
+ ispec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ json_dumpf (pe,
+ stderr,
+ JSON_INDENT (2));
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ NULL);
+ return NULL;
+ }
+
+ p_cfg = json_object_get (providers,
+ provider_url);
+ if (MHD_HTTP_OK !=
+ json_integer_value (json_object_get (p_cfg,
+ "http_status")))
+ continue;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (p_cfg,
+ pspec,
+ NULL, NULL))
+ {
+ GNUNET_break (0); /* should be impossible for well-formed state */
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "Salt unknown for selected provider");
+ return NULL;
+ }
+ id_data = json_object_get (state,
+ "identity_attributes");
+ if (NULL == id_data)
+ {
+ GNUNET_break (0); /* should be impossible for well-formed state */
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'identity_attributes' missing");
+ return NULL;
+ }
+ {
+ struct PolicyDownloadEntry *pd
+ = GNUNET_new (struct PolicyDownloadEntry);
+
+ pd->cb = cb;
+ pd->cb_cls = cb_cls;
+ pd->state = json_incref (state);
+ pd->backend_url = GNUNET_strdup (provider_url);
+ pd->recovery = ANASTASIS_recovery_begin (ANASTASIS_REDUX_ctx_,
+ id_data,
+ version,
+ pd->backend_url,
+ &provider_salt,
+ &policy_lookup_cb,
+ pd,
+ &core_early_secret_cb,
+ pd);
+ if (NULL == pd->recovery)
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR,
+ NULL);
+ GNUNET_free (pd->backend_url);
+ json_decref (pd->state);
+ GNUNET_free (pd);
+ return NULL;
+ }
+ pd->ra.cleanup = &free_pd;
+ pd->ra.cleanup_cls = pd;
+ return &pd->ra;
+ }
}
}
- if (! launched)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "No provider online, need user to specify different provider!\n");
- return_no_policy (rss,
- true);
- return;
- }
+
+ /* no provider worked */
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "selected provider is not online");
+ return NULL;
}
/**
- * Function called when the complete information about a provider
- * was added to @a new_state.
+ * The user wants us to add another provider. Download /config.
*
- * @param cls a `struct RecoveryStartStateProviderEntry`
- * @param error error code
- * @param new_state resulting new state
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb function to call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
*/
-static void
-provider_added_cb (void *cls,
- enum TALER_ErrorCode error,
- json_t *new_state)
+static struct ANASTASIS_ReduxAction *
+add_provider (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
{
- struct RecoveryStartStateProviderEntry *pe = cls;
-
- pe->ra = NULL;
- pe->istate = json_incref (new_state);
- pe->ec = error;
- pe->rss->pending--;
- if (0 == pe->rss->pending)
- providers_complete (pe->rss);
+ const char *provider_url;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("provider_url",
+ &provider_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (arguments,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break (0);
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ NULL);
+ return NULL;
+ }
+ return ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
+ state,
+ cb,
+ cb_cls);
}
/**
- * Start to query provider for recovery document.
+ * Signature of callback function that implements a state transition.
*
- * @param[in,out] rss overall recovery state
- * @param provider_url base URL of the provider to query
+ * @param state current state
+ * @param arguments arguments for the state transition
+ * @param cb function to call when done
+ * @param cb_cls closure for @a cb
*/
-static void
-begin_query_provider (struct RecoverSecretState *rss,
- const char *provider_url)
+typedef struct ANASTASIS_ReduxAction *
+(*DispatchHandler)(json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls);
+
+
+struct ANASTASIS_ReduxAction *
+ANASTASIS_recovery_action_ (json_t *state,
+ const char *action,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
{
- struct RecoveryStartStateProviderEntry *pe;
- json_t *istate;
-
- pe = GNUNET_new (struct RecoveryStartStateProviderEntry);
- pe->rss = rss;
- istate = json_object ();
- GNUNET_assert (NULL != istate);
- GNUNET_CONTAINER_DLL_insert (rss->pe_head,
- rss->pe_tail,
- pe);
- pe->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
- istate,
- &provider_added_cb,
- pe);
- json_decref (istate);
- if (NULL != pe->ra)
- rss->pending++;
+ struct Dispatcher
+ {
+ enum ANASTASIS_RecoveryState recovery_state;
+ const char *recovery_action;
+ DispatchHandler fun;
+ } dispatchers[] = {
+ {
+ ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
+ "add_provider",
+ &add_provider
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
+ "poll_providers",
+ &ANASTASIS_REDUX_poll_providers_
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
+ "select_version",
+ &done_secret_selecting
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_SECRET_SELECTING,
+ "back",
+ &ANASTASIS_back_generic_decrement_
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
+ "select_challenge",
+ &select_challenge
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
+ "sync_providers",
+ &ANASTASIS_REDUX_sync_providers_
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
+ "poll",
+ &poll_challenges
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING,
+ "back",
+ &ANASTASIS_back_generic_decrement_
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
+ "pay",
+ &pay_challenge
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_PAYING,
+ "back",
+ &ANASTASIS_back_generic_decrement_
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING,
+ "solve_challenge",
+ &solve_challenge
+ },
+ {
+ ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING,
+ "back",
+ &back_challenge_solving
+ },
+ { ANASTASIS_RECOVERY_STATE_INVALID, NULL, NULL }
+ };
+ const char *s = json_string_value (json_object_get (state,
+ "recovery_state"));
+ enum ANASTASIS_RecoveryState rs;
+
+ GNUNET_assert (NULL != s);
+ rs = ANASTASIS_recovery_state_from_string_ (s);
+ if (ANASTASIS_RECOVERY_STATE_INVALID == rs)
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
+ "'recovery_state' field invalid");
+ return NULL;
+ }
+ for (unsigned int i = 0; NULL != dispatchers[i].fun; i++)
+ {
+ if ( (rs == dispatchers[i].recovery_state) &&
+ (0 == strcmp (action,
+ dispatchers[i].recovery_action)) )
+ {
+ return dispatchers[i].fun (state,
+ arguments,
+ cb,
+ cb_cls);
+ }
+ }
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
+ action);
+ return NULL;
}
@@ -2806,11 +2631,8 @@ ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state,
ANASTASIS_ActionCallback cb,
void *cb_cls)
{
- json_t *version;
- json_t *providers;
- const json_t *attributes;
- struct RecoverSecretState *rss;
- const char *provider_url;
+ const json_t *providers;
+ json_t *attributes;
providers = json_object_get (state,
"authentication_providers");
@@ -2836,46 +2658,13 @@ ANASTASIS_REDUX_recovery_challenge_begin_ (json_t *state,
"'identity_attributes' missing");
return NULL;
}
- rss = GNUNET_new (struct RecoverSecretState);
- rss->id_data = json_incref ((json_t *) attributes);
- version = json_object_get (arguments,
- "version");
- if (NULL != version)
- {
- rss->version = (unsigned int) json_integer_value (version);
- rss->have_version = true;
- }
- rss->state = json_incref (state);
- rss->cb = cb;
- rss->cb_cls = cb_cls;
- rss->pending = 1; /* decremented after initialization loop */
-
- provider_url = json_string_value (json_object_get (arguments,
- "provider_url"));
- if (NULL != provider_url)
- {
- rss->provider_url = GNUNET_strdup (provider_url);
- begin_query_provider (rss,
- provider_url);
- }
- else
- {
- json_t *prov;
- const char *url;
-
- json_object_foreach (providers, url, prov) {
- begin_query_provider (rss,
- url);
- }
- }
- rss->pending--;
- if (0 == rss->pending)
- {
- providers_complete (rss);
- if (NULL == rss->cb)
- return NULL;
- }
- rss->ra.cleanup = &free_rss;
- rss->ra.cleanup_cls = rss;
- return &rss->ra;
+ json_object_set (state,
+ "identity_attributes",
+ attributes);
+ set_state (state,
+ ANASTASIS_RECOVERY_STATE_SECRET_SELECTING);
+ cb (cb_cls,
+ TALER_EC_NONE,
+ state);
+ return NULL;
}
diff --git a/src/reducer/anastasis_api_redux.c b/src/reducer/anastasis_api_redux.c
index f55eece..4b5ad7b 100644
--- a/src/reducer/anastasis_api_redux.c
+++ b/src/reducer/anastasis_api_redux.c
@@ -1,16 +1,16 @@
/*
This file is part of Anastasis
- Copyright (C) 2020, 2021 Anastasis SARL
+ Copyright (C) 2020, 2021, 2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
@@ -36,6 +36,12 @@
*/
#define CONFIG_GENERIC_TIMEOUT GNUNET_TIME_UNIT_MINUTES
+/**
+ * How long do we wait in a more "synchronous"
+ * scenaro for a /config reply from an Anastasis provider.
+ */
+#define CONFIG_FAST_TIMEOUT GNUNET_TIME_UNIT_SECONDS
+
#define GENERATE_STRING(STRING) #STRING,
static const char *generic_strings[] = {
@@ -132,6 +138,21 @@ struct ConfigRequest
struct ConfigReduxWaiting *w_tail;
/**
+ * When did we start?
+ */
+ struct GNUNET_TIME_Absolute start_time;
+
+ /**
+ * When do we time out?
+ */
+ struct GNUNET_TIME_Absolute timeout_at;
+
+ /**
+ * How long do we wait before trying again?
+ */
+ struct GNUNET_TIME_Relative backoff;
+
+ /**
* Obtained status code.
*/
unsigned int http_status;
@@ -152,11 +173,6 @@ struct ConfigRequest
char *business_name;
/**
- * currency used by the anastasis backend.
- */
- char *currency;
-
- /**
* Array of authorization methods supported by the server.
*/
struct AuthorizationMethodConfig *methods;
@@ -188,9 +204,9 @@ struct ConfigRequest
struct TALER_Amount liability_limit;
/**
- * Server salt.
+ * Provider salt.
*/
- struct ANASTASIS_CRYPTO_ProviderSaltP salt;
+ struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
/**
* Task to timeout /config requests.
@@ -234,6 +250,26 @@ static json_t *redux_countries;
*/
static json_t *provider_list;
+/**
+ * External reducer binary or NULL
+ * to use internal reducer.
+ */
+static char *external_reducer_binary;
+
+
+const char *
+ANASTASIS_REDUX_probe_external_reducer (void)
+{
+ if (NULL != external_reducer_binary)
+ return external_reducer_binary;
+ external_reducer_binary = getenv ("ANASTASIS_EXTERNAL_REDUCER");
+ if (NULL != external_reducer_binary)
+ unsetenv ("ANASTASIS_EXTERNAL_REDUCER");
+
+ return external_reducer_binary;
+
+}
+
/**
* Extract the mode of a state from json
@@ -262,7 +298,7 @@ ANASTASIS_generic_state_from_string_ (const char *state_string)
if (0 == strcmp (state_string,
generic_strings[i]))
return i;
- return ANASTASIS_GENERIC_STATE_ERROR;
+ return ANASTASIS_GENERIC_STATE_INVALID;
}
@@ -291,6 +327,8 @@ ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb,
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("detail",
detail)),
+ GNUNET_JSON_pack_string ("reducer_type",
+ "error"),
GNUNET_JSON_pack_uint64 ("code",
ec),
GNUNET_JSON_pack_string ("hint",
@@ -344,7 +382,6 @@ free_config_request (struct ConfigRequest *cr)
ANASTASIS_config_cancel (cr->co);
if (NULL != cr->tt)
GNUNET_SCHEDULER_cancel (cr->tt);
- GNUNET_free (cr->currency);
GNUNET_free (cr->url);
GNUNET_free (cr->business_name);
for (unsigned int i = 0; i<cr->methods_length; i++)
@@ -471,13 +508,11 @@ notify_waiting (struct ConfigRequest *cr)
"authentication_providers",
provider_list = json_object ()));
}
- provider_list = json_object_get (w->state,
- "authentication_providers");
- GNUNET_assert (NULL != provider_list);
-
if (TALER_EC_NONE != cr->ec)
{
prov = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("status",
+ "error"),
GNUNET_JSON_pack_uint64 ("error_code",
cr->ec),
GNUNET_JSON_pack_uint64 ("http_status",
@@ -503,6 +538,8 @@ notify_waiting (struct ConfigRequest *cr)
mj));
}
prov = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("status",
+ "ok"),
GNUNET_JSON_pack_array_steal ("methods",
methods_list),
TALER_JSON_pack_amount ("annual_fee",
@@ -511,14 +548,12 @@ notify_waiting (struct ConfigRequest *cr)
&cr->truth_upload_fee),
TALER_JSON_pack_amount ("liability_limit",
&cr->liability_limit),
- GNUNET_JSON_pack_string ("currency",
- cr->currency),
GNUNET_JSON_pack_string ("business_name",
cr->business_name),
GNUNET_JSON_pack_uint64 ("storage_limit_in_megabytes",
cr->storage_limit_in_megabytes),
- GNUNET_JSON_pack_data_auto ("salt",
- &cr->salt),
+ GNUNET_JSON_pack_data_auto ("provider_salt",
+ &cr->provider_salt),
GNUNET_JSON_pack_uint64 ("http_status",
cr->http_status));
}
@@ -531,68 +566,108 @@ notify_waiting (struct ConfigRequest *cr)
w->state);
abort_provider_config_cb (w);
}
+}
+
+
+/**
+ * Notify anyone waiting on @a cr that the request is done
+ * (successful or failed).
+ *
+ * @param[in,out] cls request that completed
+ */
+static void
+notify_waiting_cb (void *cls)
+{
+ struct ConfigRequest *cr = cls;
+ cr->tt = NULL;
+ notify_waiting (cr);
}
/**
+ * Function called when it is time to retry a
+ * failed /config request.
+ *
+ * @param cls the `struct ConfigRequest *` to retry.
+ */
+static void
+retry_config (void *cls);
+
+
+/**
* Function called with the results of a #ANASTASIS_get_config().
*
* @param cls closure
- * @param http_status HTTP status of the request
* @param acfg anastasis configuration
*/
static void
config_cb (void *cls,
- unsigned int http_status,
const struct ANASTASIS_Config *acfg)
{
struct ConfigRequest *cr = cls;
cr->co = NULL;
- GNUNET_SCHEDULER_cancel (cr->tt);
- cr->tt = NULL;
- cr->http_status = http_status;
- if (MHD_HTTP_OK != http_status)
- cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED;
- if ( (MHD_HTTP_OK == http_status) &&
+ if (NULL != cr->tt)
+ {
+ GNUNET_SCHEDULER_cancel (cr->tt);
+ cr->tt = NULL;
+ }
+ cr->http_status = acfg->http_status;
+ if (MHD_HTTP_OK != acfg->http_status)
+ {
+ if (0 == acfg->http_status)
+ cr->ec = TALER_EC_ANASTASIS_GENERIC_PROVIDER_UNREACHABLE;
+ else
+ cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED;
+ }
+ if ( (MHD_HTTP_OK == acfg->http_status) &&
(NULL == acfg) )
{
cr->http_status = MHD_HTTP_NOT_FOUND;
cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED;
}
- else if (NULL != acfg)
+ else
{
- if (0 == acfg->storage_limit_in_megabytes)
+ if (0 == acfg->details.ok.storage_limit_in_megabytes)
{
cr->http_status = 0;
cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG;
}
else
{
- GNUNET_free (cr->currency);
- cr->currency = GNUNET_strdup (acfg->currency);
+ cr->ec = TALER_EC_NONE;
GNUNET_free (cr->business_name);
- cr->business_name = GNUNET_strdup (acfg->business_name);
+ cr->business_name = GNUNET_strdup (acfg->details.ok.business_name);
for (unsigned int i = 0; i<cr->methods_length; i++)
GNUNET_free (cr->methods[i].type);
GNUNET_free (cr->methods);
- cr->methods = GNUNET_new_array (acfg->methods_length,
+ cr->methods = GNUNET_new_array (acfg->details.ok.methods_length,
struct AuthorizationMethodConfig);
- for (unsigned int i = 0; i<acfg->methods_length; i++)
+ for (unsigned int i = 0; i<acfg->details.ok.methods_length; i++)
{
- cr->methods[i].type = GNUNET_strdup (acfg->methods[i].type);
- cr->methods[i].usage_fee = acfg->methods[i].usage_fee;
+ cr->methods[i].type = GNUNET_strdup (acfg->details.ok.methods[i].type);
+ cr->methods[i].usage_fee = acfg->details.ok.methods[i].usage_fee;
}
- cr->methods_length = acfg->methods_length;
- cr->storage_limit_in_megabytes = acfg->storage_limit_in_megabytes;
- cr->annual_fee = acfg->annual_fee;
- cr->truth_upload_fee = acfg->truth_upload_fee;
- cr->liability_limit = acfg->liability_limit;
- cr->salt = acfg->salt;
+ cr->methods_length = acfg->details.ok.methods_length;
+ cr->storage_limit_in_megabytes =
+ acfg->details.ok.storage_limit_in_megabytes;
+ cr->annual_fee = acfg->details.ok.annual_fee;
+ cr->truth_upload_fee = acfg->details.ok.truth_upload_fee;
+ cr->liability_limit = acfg->details.ok.liability_limit;
+ cr->provider_salt = acfg->details.ok.provider_salt;
}
}
notify_waiting (cr);
+ if (MHD_HTTP_OK != acfg->http_status)
+ {
+ cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff);
+ GNUNET_assert (NULL == cr->tt);
+ GNUNET_assert (NULL != cr->url);
+ cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff,
+ &retry_config,
+ cr);
+ }
}
@@ -607,22 +682,57 @@ config_request_timeout (void *cls)
struct ConfigRequest *cr = cls;
cr->tt = NULL;
- ANASTASIS_config_cancel (cr->co);
- cr->co = NULL;
+ if (NULL != cr->co)
+ {
+ ANASTASIS_config_cancel (cr->co);
+ cr->co = NULL;
+ }
cr->http_status = 0;
cr->ec = TALER_EC_GENERIC_TIMEOUT;
notify_waiting (cr);
+ cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff);
+ GNUNET_assert (NULL == cr->tt);
+ GNUNET_assert (NULL != cr->url);
+ cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff,
+ &retry_config,
+ cr);
+}
+
+
+static void
+retry_config (void *cls)
+{
+ struct ConfigRequest *cr = cls;
+
+ cr->tt = NULL;
+ if (NULL != cr->co)
+ {
+ ANASTASIS_config_cancel (cr->co);
+ cr->co = NULL;
+ }
+ cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT);
+ GNUNET_assert (NULL == cr->tt);
+ cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
+ &config_request_timeout,
+ cr);
+ cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_,
+ cr->url,
+ &config_cb,
+ cr);
+ GNUNET_break (NULL != cr->co);
}
/**
* Schedule job to obtain Anastasis provider configuration at @a url.
*
+ * @param timeout how long to wait for a reply
* @param url base URL of Anastasis provider
* @return check config handle
*/
static struct ConfigRequest *
-check_config (const char *url)
+check_config (struct GNUNET_TIME_Relative timeout,
+ const char *url)
{
struct ConfigRequest *cr;
@@ -632,17 +742,47 @@ check_config (const char *url)
cr->url))
continue;
if (NULL != cr->co)
+ {
+ struct GNUNET_TIME_Relative duration;
+ struct GNUNET_TIME_Relative left;
+ struct GNUNET_TIME_Relative xleft;
+
+ duration = GNUNET_TIME_absolute_get_duration (cr->start_time);
+ left = GNUNET_TIME_relative_subtract (timeout,
+ duration);
+ xleft = GNUNET_TIME_absolute_get_remaining (cr->timeout_at);
+ if (GNUNET_TIME_relative_cmp (left,
+ <,
+ xleft))
+ {
+ /* new timeout is shorter! */
+ cr->timeout_at = GNUNET_TIME_relative_to_absolute (left);
+ GNUNET_SCHEDULER_cancel (cr->tt);
+ cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
+ &config_request_timeout,
+ cr);
+ }
return cr; /* already on it */
+ }
break;
}
if (NULL == cr)
{
cr = GNUNET_new (struct ConfigRequest);
+ cr->start_time = GNUNET_TIME_absolute_get ();
cr->url = GNUNET_strdup (url);
GNUNET_CONTAINER_DLL_insert (cr_head,
cr_tail,
cr);
}
+ if (MHD_HTTP_OK == cr->http_status)
+ return cr;
+ cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout);
+ if (NULL != cr->tt)
+ GNUNET_SCHEDULER_cancel (cr->tt);
+ cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at,
+ &config_request_timeout,
+ cr);
cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_,
cr->url,
&config_cb,
@@ -652,12 +792,6 @@ check_config (const char *url)
GNUNET_break (0);
return NULL;
}
- else
- {
- cr->tt = GNUNET_SCHEDULER_add_delayed (CONFIG_GENERIC_TIMEOUT,
- &config_request_timeout,
- cr);
- }
return cr;
}
@@ -665,12 +799,12 @@ check_config (const char *url)
/**
* Begin asynchronous check for provider configurations.
*
- * @param currencies the currencies to initiate the provider checks for
+ * @param cc country code that was selected
* @param[in,out] state to set provider list for
* @return #TALER_EC_NONE on success
*/
static enum TALER_ErrorCode
-begin_provider_config_check (const json_t *currencies,
+begin_provider_config_check (const char *cc,
json_t *state)
{
if (NULL == provider_list)
@@ -718,14 +852,17 @@ begin_provider_config_check (const json_t *currencies,
json_array_foreach (provider_arr, index, provider)
{
const char *url;
- const char *cur;
+ const char *restricted = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("url",
&url),
- GNUNET_JSON_spec_string ("currency",
- &cur),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("restricted",
+ &restricted),
+ NULL),
GNUNET_JSON_spec_end ()
};
+ json_t *prov;
if (GNUNET_OK !=
GNUNET_JSON_parse (provider,
@@ -736,33 +873,35 @@ begin_provider_config_check (const json_t *currencies,
json_decref (pl);
return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED;
}
-
+ if ( (NULL != restricted) &&
+ (0 != strcmp (restricted,
+ cc)) )
{
- bool found = false;
- json_t *cu;
- size_t off;
-
- json_array_foreach (currencies, off, cu)
- {
- const char *currency;
-
- currency = json_string_value (cu);
- if (NULL == currency)
- {
- json_decref (pl);
- return TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID;
- }
- found = (0 == strcasecmp (currency,
- cur));
- }
- if (! found)
- continue;
+ /* skip */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Skipping provider restricted to country `%s'\n",
+ restricted);
+ continue;
+ }
+ if ( (NULL == restricted) &&
+ (0 == strcmp (cc,
+ "xx")) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Running in demo mode, skipping unrestricted providers\n");
+ /* demo mode, skipping regular providers */
+ continue;
}
+ prov = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("status",
+ "not-contacted"));
+ GNUNET_assert (NULL != prov);
GNUNET_assert (0 ==
json_object_set_new (pl,
url,
- json_object ()));
- check_config (url);
+ prov));
+ check_config (CONFIG_GENERIC_TIMEOUT,
+ url);
}
GNUNET_assert (0 ==
json_object_set_new (state,
@@ -990,7 +1129,6 @@ select_country (json_t *state,
{
const json_t *required_attrs;
const json_t *country_code;
- const json_t *currencies;
const json_t *redux_id_attr;
if (NULL == arguments)
@@ -1040,23 +1178,11 @@ select_country (json_t *state,
}
}
- currencies = json_object_get (arguments,
- "currencies");
- if ( (NULL == currencies) ||
- (! json_is_array (currencies)) )
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
- "'currencies' missing");
- return NULL;
- }
- /* We now have an idea of the currency, begin fetching
- provider /configs (we likely need them later) */
+ /* Begin fetching provider /configs (we likely need them later) */
{
enum TALER_ErrorCode ec;
- ec = begin_provider_config_check (currencies,
+ ec = begin_provider_config_check (json_string_value (country_code),
state);
if (TALER_EC_NONE != ec)
{
@@ -1096,10 +1222,6 @@ select_country (json_t *state,
(json_t *) country_code));
GNUNET_assert (0 ==
json_object_set (state,
- "currencies",
- (json_t *) currencies));
- GNUNET_assert (0 ==
- json_object_set (state,
"required_attributes",
(json_t *) required_attrs));
cb (cb_cls,
@@ -1143,7 +1265,8 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url,
struct ConfigRequest *cr;
struct ConfigReduxWaiting *w;
- cr = check_config (url);
+ cr = check_config (CONFIG_FAST_TIMEOUT,
+ url);
w = GNUNET_new (struct ConfigReduxWaiting);
w->cr = cr;
w->state = json_incref (state);
@@ -1156,8 +1279,10 @@ ANASTASIS_REDUX_add_provider_to_state_ (const char *url,
w);
if (NULL == cr->co)
{
- notify_waiting (cr);
- return NULL;
+ if (NULL != cr->tt)
+ GNUNET_SCHEDULER_cancel (cr->tt);
+ cr->tt = GNUNET_SCHEDULER_add_now (&notify_waiting_cb,
+ cr);
}
return &w->ra;
}
@@ -1228,19 +1353,22 @@ enter_user_attributes (json_t *state,
const char *attribute_value;
const char *regexp = NULL;
const char *reglog = NULL;
- int optional = false;
+ bool optional = false;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("name",
&name),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("validation-regex",
- &regexp)),
+ &regexp),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("validation-logic",
- &reglog)),
+ &reglog),
+ NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_boolean ("optional",
- &optional)),
+ GNUNET_JSON_spec_bool ("optional",
+ &optional),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -1378,9 +1506,16 @@ ANASTASIS_add_provider_ (json_t *state,
ANASTASIS_ActionCallback cb,
void *cb_cls)
{
- json_t *urls;
json_t *tlist;
+ if (NULL == arguments)
+ {
+ ANASTASIS_redux_fail_ (cb,
+ cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
+ "arguments missing");
+ return true; /* cb was invoked */
+ }
tlist = json_object_get (state,
"authentication_providers");
if (NULL == tlist)
@@ -1392,47 +1527,19 @@ ANASTASIS_add_provider_ (json_t *state,
"authentication_providers",
tlist));
}
- if (NULL == arguments)
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "arguments missing");
- return true;
- }
- urls = json_object_get (arguments,
- "urls");
- if (NULL == urls)
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "'urls' missing");
- return true;
- }
{
- size_t index;
- json_t *url;
+ json_t *params;
+ const char *url;
- json_array_foreach (urls, index, url)
+ json_object_foreach (((json_t *) arguments), url, params)
{
- const char *url_str = json_string_value (url);
-
- if (NULL == url_str)
- {
- ANASTASIS_redux_fail_ (cb,
- cb_cls,
- TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID,
- "'urls' must be strings");
- return true;
- }
GNUNET_assert (0 ==
- json_object_set_new (tlist,
- url_str,
- json_object ()));
+ json_object_set (tlist,
+ url,
+ params));
}
}
- return false;
+ return false; /* cb not invoked */
}
@@ -1502,6 +1609,256 @@ typedef struct ANASTASIS_ReduxAction *
void *cb_cls);
+/**
+ * Closure for read operations on the external reducer.
+ */
+struct ExternalReducerCls
+{
+ struct GNUNET_Buffer read_buffer;
+ struct GNUNET_SCHEDULER_Task *read_task;
+ struct GNUNET_DISK_PipeHandle *reducer_stdin;
+ struct GNUNET_DISK_PipeHandle *reducer_stdout;
+ struct GNUNET_OS_Process *reducer_process;
+ ANASTASIS_ActionCallback action_cb;
+ void *action_cb_cls;
+};
+
+/**
+ * Clean up and destroy the external reducer state.
+ *
+ * @param cls closure, a 'struct ExternalReducerCls *'
+ */
+static void
+cleanup_external_reducer (void *cls)
+{
+ struct ExternalReducerCls *red_cls = cls;
+
+ if (NULL != red_cls->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (red_cls->read_task);
+ red_cls->read_task = NULL;
+ }
+
+ GNUNET_buffer_clear (&red_cls->read_buffer);
+ if (NULL != red_cls->reducer_stdin)
+ {
+ GNUNET_DISK_pipe_close (red_cls->reducer_stdin);
+ red_cls->reducer_stdin = NULL;
+ }
+ if (NULL != red_cls->reducer_stdout)
+ {
+ GNUNET_DISK_pipe_close (red_cls->reducer_stdout);
+ red_cls->reducer_stdout = NULL;
+ }
+
+ if (NULL != red_cls->reducer_process)
+ {
+ enum GNUNET_OS_ProcessStatusType type;
+ unsigned long code;
+ enum GNUNET_GenericReturnValue pwret;
+
+ pwret = GNUNET_OS_process_wait_status (red_cls->reducer_process,
+ &type,
+ &code);
+
+ GNUNET_assert (GNUNET_SYSERR != pwret);
+ if (GNUNET_NO == pwret)
+ {
+ GNUNET_OS_process_kill (red_cls->reducer_process,
+ SIGTERM);
+ GNUNET_assert (GNUNET_SYSERR != GNUNET_OS_process_wait (
+ red_cls->reducer_process));
+ }
+
+ GNUNET_OS_process_destroy (red_cls->reducer_process);
+ red_cls->reducer_process = NULL;
+ }
+
+ GNUNET_free (red_cls);
+}
+
+
+/**
+ * Task called when
+ *
+ * @param cls closure, a 'struct ExternalReducerCls *'
+ */
+static void
+external_reducer_read_cb (void *cls)
+{
+ struct ExternalReducerCls *red_cls = cls;
+ ssize_t sret;
+ char buf[256];
+
+ red_cls->read_task = NULL;
+
+ sret = GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (
+ red_cls->reducer_stdout,
+ GNUNET_DISK_PIPE_END_READ),
+ buf,
+ 256);
+ if (sret < 0)
+ {
+ GNUNET_break (0);
+ red_cls->action_cb (red_cls->action_cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR,
+ NULL);
+ cleanup_external_reducer (red_cls);
+ return;
+ }
+ else if (0 == sret)
+ {
+ char *str = GNUNET_buffer_reap_str (&red_cls->read_buffer);
+ json_t *json;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got external reducer response: '%s'\n",
+ str);
+
+ json = json_loads (str, 0, NULL);
+
+ if (NULL == json)
+ {
+ GNUNET_break (0);
+ red_cls->action_cb (red_cls->action_cb_cls,
+ TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR,
+ NULL);
+ cleanup_external_reducer (red_cls);
+ return;
+ }
+
+ {
+ enum TALER_ErrorCode ec;
+ ec = json_integer_value (json_object_get (json, "code"));
+
+ red_cls->action_cb (red_cls->action_cb_cls,
+ ec,
+ json);
+ }
+ cleanup_external_reducer (red_cls);
+ return;
+ }
+ else
+ {
+ GNUNET_buffer_write (&red_cls->read_buffer,
+ buf,
+ sret);
+
+ red_cls->read_task = GNUNET_SCHEDULER_add_read_file (
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_DISK_pipe_handle (
+ red_cls->reducer_stdout,
+ GNUNET_DISK_PIPE_END_READ),
+ external_reducer_read_cb,
+ red_cls);
+ }
+}
+
+
+/**
+ * Handle an action using an external reducer, i.e.
+ * by shelling out to another process.
+ */
+static struct ANASTASIS_ReduxAction *
+redux_action_external (const char *ext_reducer,
+ const json_t *state,
+ const char *action,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls)
+{
+ char *arg_str;
+ char *state_str = json_dumps (state, JSON_COMPACT);
+ ssize_t sret;
+ struct ExternalReducerCls *red_cls = GNUNET_new (struct ExternalReducerCls);
+
+ if (NULL == arguments)
+ arg_str = GNUNET_strdup ("{}");
+ else
+ arg_str = json_dumps (arguments, JSON_COMPACT);
+
+ red_cls->action_cb = cb;
+ red_cls->action_cb_cls = cb_cls;
+
+ GNUNET_assert (NULL != (red_cls->reducer_stdin = GNUNET_DISK_pipe (
+ GNUNET_DISK_PF_NONE)));
+ GNUNET_assert (NULL != (red_cls->reducer_stdout = GNUNET_DISK_pipe (
+ GNUNET_DISK_PF_NONE)));
+
+ /* By the time we're here, this variable should be unset, because
+ otherwise using anastasis-reducer as the external reducer
+ will lead to infinite recursion. */
+ GNUNET_assert (NULL == getenv ("ANASTASIS_EXTERNAL_REDUCER"));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting external reducer with action '%s' and argument '%s'\n",
+ action,
+ arg_str);
+
+ red_cls->reducer_process = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
+ red_cls->reducer_stdin,
+ red_cls->reducer_stdout,
+ NULL,
+ ext_reducer,
+ ext_reducer,
+ "-a",
+ arg_str,
+ action,
+ NULL);
+
+ GNUNET_free (arg_str);
+
+ if (NULL == red_cls->reducer_process)
+ {
+ GNUNET_break (0);
+ GNUNET_free (state_str);
+ cleanup_external_reducer (red_cls);
+ return NULL;
+ }
+
+ /* Close pipe ends we don't use. */
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin,
+ GNUNET_DISK_PIPE_END_READ));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_pipe_close_end (red_cls->reducer_stdout,
+ GNUNET_DISK_PIPE_END_WRITE));
+
+ sret = GNUNET_DISK_file_write_blocking (GNUNET_DISK_pipe_handle (
+ red_cls->reducer_stdin,
+ GNUNET_DISK_PIPE_END_WRITE),
+ state_str,
+ strlen (state_str));
+ GNUNET_free (state_str);
+ if (sret <= 0)
+ {
+ GNUNET_break (0);
+ cleanup_external_reducer (red_cls);
+ return NULL;
+ }
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin,
+ GNUNET_DISK_PIPE_END_WRITE));
+
+ red_cls->read_task = GNUNET_SCHEDULER_add_read_file (
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_DISK_pipe_handle (
+ red_cls->reducer_stdout,
+ GNUNET_DISK_PIPE_END_READ),
+ external_reducer_read_cb,
+ red_cls);
+
+ {
+ struct ANASTASIS_ReduxAction *ra = GNUNET_new (struct
+ ANASTASIS_ReduxAction);
+ ra->cleanup_cls = red_cls;
+ ra->cleanup = cleanup_external_reducer;
+ return ra;
+ }
+}
+
+
struct ANASTASIS_ReduxAction *
ANASTASIS_redux_action (const json_t *state,
const char *action,
@@ -1520,6 +1877,7 @@ ANASTASIS_redux_action (const json_t *state,
"select_continent",
&select_continent
},
+ /* Deprecated alias for "back" from that state, should be removed eventually. */
{
ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
"unselect_continent",
@@ -1527,6 +1885,11 @@ ANASTASIS_redux_action (const json_t *state,
},
{
ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
+ "back",
+ &unselect_continent
+ },
+ {
+ ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
"select_country",
&select_country
},
@@ -1536,11 +1899,6 @@ ANASTASIS_redux_action (const json_t *state,
&select_continent
},
{
- ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING,
- "unselect_continent",
- &unselect_continent
- },
- {
ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING,
"enter_user_attributes",
&enter_user_attributes
@@ -1555,13 +1913,25 @@ ANASTASIS_redux_action (const json_t *state,
"back",
&ANASTASIS_back_generic_decrement_
},
- { ANASTASIS_GENERIC_STATE_ERROR, NULL, NULL }
+ { ANASTASIS_GENERIC_STATE_INVALID, NULL, NULL }
};
bool recovery_mode = false;
const char *s = json_string_value (json_object_get (state,
"backup_state"));
enum ANASTASIS_GenericState gs;
+ /* If requested, handle action with external reducer, used for testing. */
+ {
+ const char *ext_reducer = ANASTASIS_REDUX_probe_external_reducer ();
+ if (NULL != ext_reducer)
+ return redux_action_external (ext_reducer,
+ state,
+ action,
+ arguments,
+ cb,
+ cb_cls);
+ }
+
if (NULL == s)
{
s = json_string_value (json_object_get (state,
@@ -1583,7 +1953,7 @@ ANASTASIS_redux_action (const json_t *state,
new_state = json_deep_copy (state);
GNUNET_assert (NULL != new_state);
- if (gs != ANASTASIS_GENERIC_STATE_ERROR)
+ if (gs != ANASTASIS_GENERIC_STATE_INVALID)
{
for (unsigned int i = 0; NULL != dispatchers[i].fun; i++)
{
@@ -1737,3 +2107,68 @@ ANASTASIS_REDUX_load_continents_ ()
GNUNET_JSON_pack_array_steal ("continents",
continents));
}
+
+
+/**
+ * Lookup @a provider_salt of @a provider_url in @a state.
+ *
+ * @param state the state to inspect
+ * @param provider_url provider to look into
+ * @param[out] provider_salt value to extract
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+ANASTASIS_reducer_lookup_salt (
+ const json_t *state,
+ const char *provider_url,
+ struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt)
+{
+ const json_t *aps;
+ const json_t *cfg;
+ uint32_t http_status = 0;
+ const char *status;
+ bool no_salt;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("status",
+ &status),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("http_status",
+ &http_status),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto ("provider_salt",
+ provider_salt),
+ &no_salt),
+ GNUNET_JSON_spec_end ()
+ };
+
+ aps = json_object_get (state,
+ "authentication_providers");
+ if (NULL == aps)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ cfg = json_object_get (aps,
+ provider_url);
+ if (NULL == cfg)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (cfg,
+ spec,
+ NULL, NULL))
+ {
+ /* provider not working */
+ GNUNET_break_op (0);
+ return GNUNET_NO;
+ }
+ if (0 == strcmp (status,
+ "disabled"))
+ return GNUNET_NO;
+ if (no_salt)
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
diff --git a/src/reducer/anastasis_api_redux.h b/src/reducer/anastasis_api_redux.h
index 4d62d5e..03eef33 100644
--- a/src/reducer/anastasis_api_redux.h
+++ b/src/reducer/anastasis_api_redux.h
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
@@ -25,7 +25,7 @@
#define ANASTASIS_GENERIC_STATES(REDUX_STATE) \
- REDUX_STATE (ERROR) \
+ REDUX_STATE (INVALID) \
REDUX_STATE (CONTINENT_SELECTING) \
REDUX_STATE (COUNTRY_SELECTING) \
REDUX_STATE (USER_ATTRIBUTES_COLLECTING)
@@ -108,6 +108,42 @@ ANASTASIS_REDUX_load_continents_ (void);
/**
+ * Try to obtain configuration information on all configured
+ * providers. Upon success, call @a cb with the updated provider
+ * status data.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_poll_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Check if we have information on all providers involved in
+ * a recovery procedure, and if not, try to obtain it. Upon
+ * success, call @a cb with the updated provider status data.
+ *
+ * @param[in] state we are in
+ * @param arguments our arguments with the solution
+ * @param cb functiont o call with the new state
+ * @param cb_cls closure for @a cb
+ * @return handle to cancel challenge selection step
+ */
+struct ANASTASIS_ReduxAction *
+ANASTASIS_REDUX_sync_providers_ (json_t *state,
+ const json_t *arguments,
+ ANASTASIS_ActionCallback cb,
+ void *cb_cls);
+
+
+/**
* Returns the enum value to a string value of a state.
*
* @param state_string string to convert
@@ -168,6 +204,20 @@ ANASTASIS_recovery_state_to_string_ (enum ANASTASIS_RecoveryState rs);
/**
+ * Lookup @a salt of @a provider_url in @a state.
+ *
+ * @param state the state to inspect
+ * @param provider_url provider to look into
+ * @param[out] salt value to extract
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+ANASTASIS_reducer_lookup_salt (const json_t *state,
+ const char *provider_url,
+ struct ANASTASIS_CRYPTO_ProviderSaltP *salt);
+
+
+/**
* Function to return a json error response.
*
* @param cb callback to give error to
@@ -328,6 +378,16 @@ ANASTASIS_backup_action_ (json_t *state,
/**
+ * Check if an external reducer binary is requested.
+ * Cache the result and unset the corresponding environment
+ * variable.
+ *
+ * @returns name of the external reducer or NULL to user internal reducer
+ */
+const char *
+ANASTASIS_REDUX_probe_external_reducer (void);
+
+/**
* Generic container for an action with asynchronous activities.
*/
struct ANASTASIS_ReduxAction
diff --git a/src/reducer/validation_CH_AHV.c b/src/reducer/validation_CH_AHV.c
index 6beded1..4ea973c 100644
--- a/src/reducer/validation_CH_AHV.c
+++ b/src/reducer/validation_CH_AHV.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_CZ_BN.c b/src/reducer/validation_CZ_BN.c
index 85dea4a..b570841 100644
--- a/src/reducer/validation_CZ_BN.c
+++ b/src/reducer/validation_CZ_BN.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_DE_SVN.c b/src/reducer/validation_DE_SVN.c
index 7096708..e753f0c 100644
--- a/src/reducer/validation_DE_SVN.c
+++ b/src/reducer/validation_DE_SVN.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_DE_TIN.c b/src/reducer/validation_DE_TIN.c
index 0412f87..5678579 100644
--- a/src/reducer/validation_DE_TIN.c
+++ b/src/reducer/validation_DE_TIN.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_ES_DNI.c b/src/reducer/validation_ES_DNI.c
new file mode 100644
index 0000000..5fb3885
--- /dev/null
+++ b/src/reducer/validation_ES_DNI.c
@@ -0,0 +1,184 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2021 Anastasis SARL
+
+ Anastasis 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.
+
+ Anastasis 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
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/validation_ES_DNI.c
+ * @brief validation logic for Spanish Documento Nacional de Identidad numbers, and Número de Identificación de Extranjeros
+ * @author Christian Grothoff
+ *
+ * Examples:
+ * 12345678Z, 39740191D, 14741806W, X8095495R
+ */
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+/**
+ * Function to validate a Spanish CIF number.
+ *
+ * @param civ number to validate
+ * @return true if validation passed, else false
+ */
+static bool
+validate_cif (const char *cif)
+{
+ size_t slen = strlen (cif);
+ char letter = cif[0];
+ const char *number = &cif[1];
+ char control = cif[slen - 1];
+ unsigned int sum = 0;
+
+ if (9 != slen)
+ return false;
+
+ for (unsigned int i = 0; i < slen - 2; i++)
+ {
+ unsigned int n = number[i] - '0';
+
+ if (n >= 10)
+ return false;
+ if (0 == (i % 2))
+ {
+ n *= 2;
+ sum += n < 10 ? n : n - 9;
+ }
+ else
+ {
+ sum += n;
+ }
+ }
+ sum %= 10;
+ if (0 != sum)
+ sum = 10 - sum;
+ {
+ char control_digit = "0123456789"[sum];
+ char control_letter = "JABCDEFGHI"[sum];
+
+ switch (letter)
+ {
+ case 'A':
+ case 'B':
+ case 'E':
+ case 'H':
+ return control == control_digit;
+ case 'N':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'W':
+ return control == control_letter;
+ default:
+ return (control == control_letter) ||
+ (control == control_digit);
+ }
+ }
+}
+
+
+/**
+ * Function to validate a Spanish DNI number.
+ *
+ * See https://www.ordenacionjuego.es/en/calculo-digito-control
+ *
+ * @param dni_number number to validate (input)
+ * @return true if validation passed, else false
+ */
+bool
+ES_DNI_check (const char *dni_number)
+{
+ const char map[] = "TRWAGMYFPDXBNJZSQVHLCKE";
+ unsigned int num;
+ char chksum;
+ unsigned int fact;
+ char dummy;
+
+ if (strlen (dni_number) < 8)
+ return false;
+ switch (dni_number[0])
+ {
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ /* CIF: [A-W]\d{7}[0-9A-J] */
+ /* CIF is for companies, we only take those
+ of individuals here! */
+ return false; /* CIV is not allowed! */
+ case 'M':
+ /* special NIE, with CIF validation (?),
+ but for individuals, see
+ https://www.strongabogados.com/tax-id-spain.php */
+ return validate_cif (dni_number);
+ case 'X':
+ case 'Y':
+ case 'Z':
+ /* NIE */
+ fact = dni_number[0] - 'X';
+ /* 7 or 8 digits */
+ if (2 == sscanf (&dni_number[1],
+ "%8u%c%c",
+ &num,
+ &chksum,
+ &dummy))
+ {
+ num += fact * 100000000;
+ }
+ else if (2 == sscanf (&dni_number[1],
+ "%7u%c%c",
+ &num,
+ &chksum,
+ &dummy))
+ {
+ num += fact * 10000000;
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ default:
+ fact = 0;
+ /* DNI */
+ if (2 != sscanf (dni_number,
+ "%8u%c%c",
+ &num,
+ &chksum,
+ &dummy))
+ return false;
+ break;
+ }
+ if (map[num % 23] != chksum)
+ return false;
+ return true;
+}
diff --git a/src/reducer/validation_FR_INSEE.c b/src/reducer/validation_FR_INSEE.c
new file mode 100644
index 0000000..19d81fd
--- /dev/null
+++ b/src/reducer/validation_FR_INSEE.c
@@ -0,0 +1,66 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2022 Anastasis SARL
+
+ Anastasis 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.
+
+ Anastasis 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
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/validation_FR_INSEE.c
+ * @brief Validation for French INSEE Numbers
+ * @author Christian Grothoff
+ */
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+
+/**
+ * Function to validate a French INSEE number.
+ *
+ * See https://en.wikipedia.org/wiki/INSEE_code
+ *
+ * Note that we do not implement checks on the month
+ * and also allow non-binary prefixes.
+ *
+ * @param insee_number social security number to validate (input)
+ * @return true if validation passed, else false
+ */
+bool
+FR_INSEE_check (const char *insee_number)
+{
+ char pfx[14];
+ unsigned long long num;
+ unsigned int cc;
+ char dum;
+
+ if (strlen (insee_number) != 15)
+ return false;
+ memcpy (pfx,
+ insee_number,
+ 13);
+ pfx[13] = '\0';
+ if (1 !=
+ sscanf (pfx,
+ "%llu%c",
+ &num,
+ &dum))
+ return false;
+ if (1 !=
+ sscanf (&insee_number[13],
+ "%u%c",
+ &cc,
+ &dum))
+ return false;
+ if (97 - cc != num % 97)
+ return false;
+ return true;
+}
diff --git a/src/reducer/validation_IN_AADHAR.c b/src/reducer/validation_IN_AADHAR.c
index 4c4901a..d53b655 100644
--- a/src/reducer/validation_IN_AADHAR.c
+++ b/src/reducer/validation_IN_AADHAR.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_IT_CF.c b/src/reducer/validation_IT_CF.c
index ca66a11..6e9c6c6 100644
--- a/src/reducer/validation_IT_CF.c
+++ b/src/reducer/validation_IT_CF.c
@@ -3,14 +3,14 @@
Copyright (C) 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_NL_BSN.c b/src/reducer/validation_NL_BSN.c
new file mode 100644
index 0000000..f92bb38
--- /dev/null
+++ b/src/reducer/validation_NL_BSN.c
@@ -0,0 +1,57 @@
+/*
+ This file is part of Anastasis
+ Copyright (C) 2020, 2021 Anastasis SARL
+
+ Anastasis 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.
+
+ Anastasis 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
+ Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/validation_NL_BSN.c
+ * @brief Validation for Dutch Buergerservicenummern
+ * @author Christian Grothoff
+ */
+#include <string.h>
+#include <stdbool.h>
+
+
+/**
+ * Function to validate a Dutch Social Security number.
+ *
+ * See https://nl.wikipedia.org/wiki/Burgerservicenummer
+ *
+ * @param bsn_number social security number to validate (input)
+ * @return true if validation passed, else false
+ */
+bool
+NL_BSN_check (const char *bsn_number)
+{
+ static const int factors[] = {
+ 9, 8, 7, 6, 5, 4, 3, 2, -1
+ };
+ unsigned int sum = 0;
+
+ if (strlen (bsn_number) != 9)
+ return false;
+ for (unsigned int i = 0; i<8; i++)
+ {
+ unsigned char c = (unsigned char) bsn_number[i];
+
+ if ( ('0' > c) || ('9' < c) )
+ return false;
+ sum += (c - '0') * factors[i];
+ }
+ {
+ unsigned char c = (unsigned char) bsn_number[8];
+ unsigned int v = (c - '0');
+
+ return (sum % 11 == v);
+ }
+}
diff --git a/src/reducer/validation_XX_SQUARE.c b/src/reducer/validation_XX_SQUARE.c
index 88cf890..1f43400 100644
--- a/src/reducer/validation_XX_SQUARE.c
+++ b/src/reducer/validation_XX_SQUARE.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**
diff --git a/src/reducer/validation_XY_PRIME.c b/src/reducer/validation_XY_PRIME.c
index 32bdce0..56aa724 100644
--- a/src/reducer/validation_XY_PRIME.c
+++ b/src/reducer/validation_XY_PRIME.c
@@ -3,14 +3,14 @@
Copyright (C) 2020, 2021 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
- terms of the GNU Lesser General Public License as published by the Free Software
+ terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis 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.
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
+ You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/>
*/
/**