diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-09-28 15:50:28 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-09-28 15:50:28 +0200 |
commit | 435950ee10fc3d58f7ff992a2c2a2a3f73efa806 (patch) | |
tree | c0f900b8ff04e4f5c9d9ec50d0657a76ea41372d | |
parent | 3c3984cdcb88d5d3eb568d7e9647a2f2c473c0bc (diff) | |
download | anastasis-435950ee10fc3d58f7ff992a2c2a2a3f73efa806.tar.gz anastasis-435950ee10fc3d58f7ff992a2c2a2a3f73efa806.zip |
theoretically, this completes the TOTP implementation, alas completely untested
-rw-r--r-- | src/authorization/anastasis_authorization_plugin_totp.c | 47 | ||||
-rw-r--r-- | src/backend/anastasis-httpd_truth.c | 374 | ||||
-rw-r--r-- | src/include/anastasis_database_plugin.h | 2 | ||||
-rw-r--r-- | src/reducer/anastasis_api_recovery_redux.c | 9 |
4 files changed, 300 insertions, 132 deletions
diff --git a/src/authorization/anastasis_authorization_plugin_totp.c b/src/authorization/anastasis_authorization_plugin_totp.c index 6fcdd39..ee1ab3f 100644 --- a/src/authorization/anastasis_authorization_plugin_totp.c +++ b/src/authorization/anastasis_authorization_plugin_totp.c | |||
@@ -59,14 +59,14 @@ struct ANASTASIS_AUTHORIZATION_State | |||
59 | struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; | 59 | struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; |
60 | 60 | ||
61 | /** | 61 | /** |
62 | * Our context. | 62 | * Was the challenge satisfied? |
63 | */ | 63 | */ |
64 | const struct ANASTASIS_AuthorizationContext *ac; | 64 | struct GNUNET_HashCode valid_replies[TIME_INTERVAL_RANGE * 2 + 1]; |
65 | 65 | ||
66 | /** | 66 | /** |
67 | * Was the challenge satisfied? | 67 | * Our context. |
68 | */ | 68 | */ |
69 | bool ok; | 69 | const struct ANASTASIS_AuthorizationContext *ac; |
70 | 70 | ||
71 | }; | 71 | }; |
72 | 72 | ||
@@ -212,9 +212,9 @@ compute_totp (int time_off, | |||
212 | * @param trigger_cls closure for @a trigger | 212 | * @param trigger_cls closure for @a trigger |
213 | * @param truth_uuid Identifier of the challenge, to be (if possible) included in the | 213 | * @param truth_uuid Identifier of the challenge, to be (if possible) included in the |
214 | * interaction with the user | 214 | * interaction with the user |
215 | * @param code set to secret code that the user provided to satisfy the challenge in | 215 | * @param code always 0 (direct validation, backend does |
216 | * the main anastasis protocol | 216 | * not generate a code in this mode) |
217 | * @param data input to validate (i.e. the shared secret) | 217 | * @param data truth for input to validate (i.e. the shared secret) |
218 | * @param data_length number of bytes in @a data | 218 | * @param data_length number of bytes in @a data |
219 | * @return state to track progress on the authorization operation, NULL on failure | 219 | * @return state to track progress on the authorization operation, NULL on failure |
220 | */ | 220 | */ |
@@ -230,7 +230,9 @@ totp_start (void *cls, | |||
230 | const struct ANASTASIS_AuthorizationContext *ac = cls; | 230 | const struct ANASTASIS_AuthorizationContext *ac = cls; |
231 | struct ANASTASIS_AUTHORIZATION_State *as; | 231 | struct ANASTASIS_AUTHORIZATION_State *as; |
232 | uint64_t want; | 232 | uint64_t want; |
233 | unsigned int off = 0; | ||
233 | 234 | ||
235 | GNUNET_assert (0 == code); | ||
234 | as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State); | 236 | as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State); |
235 | as->ac = ac; | 237 | as->ac = ac; |
236 | as->truth_uuid = *truth_uuid; | 238 | as->truth_uuid = *truth_uuid; |
@@ -241,8 +243,8 @@ totp_start (void *cls, | |||
241 | want = compute_totp (i, | 243 | want = compute_totp (i, |
242 | data, | 244 | data, |
243 | data_length); | 245 | data_length); |
244 | if (code == want) | 246 | ANASTASIS_hash_answer (want, |
245 | as->ok = true; | 247 | &as->valid_replies[off++]); |
246 | } | 248 | } |
247 | return as; | 249 | return as; |
248 | } | 250 | } |
@@ -264,9 +266,32 @@ totp_process (struct ANASTASIS_AUTHORIZATION_State *as, | |||
264 | MHD_RESULT mres; | 266 | MHD_RESULT mres; |
265 | const char *mime; | 267 | const char *mime; |
266 | const char *lang; | 268 | const char *lang; |
269 | const char *challenge_response_s; | ||
270 | struct GNUNET_HashCode challenge_response; | ||
271 | |||
272 | challenge_response_s = MHD_lookup_connection_value (connection, | ||
273 | MHD_GET_ARGUMENT_KIND, | ||
274 | "response"); | ||
275 | if ( (NULL == challenge_response_s) || | ||
276 | (GNUNET_OK != | ||
277 | GNUNET_CRYPTO_hash_from_string (challenge_response_s, | ||
278 | &challenge_response)) ) | ||
279 | { | ||
280 | GNUNET_break_op (0); | ||
281 | mres = TALER_MHD_reply_with_error (connection, | ||
282 | MHD_HTTP_BAD_REQUEST, | ||
283 | TALER_EC_GENERIC_PARAMETER_MALFORMED, | ||
284 | "response"); | ||
285 | if (MHD_YES != mres) | ||
286 | return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED; | ||
287 | return ANASTASIS_AUTHORIZATION_RES_FAILED; | ||
267 | 288 | ||
268 | if (as->ok) | 289 | } |
269 | return ANASTASIS_AUTHORIZATION_RES_FINISHED; | 290 | for (unsigned int i = 0; i<=TIME_INTERVAL_RANGE * 2; i++) |
291 | if (0 == | ||
292 | GNUNET_memcmp (&challenge_response, | ||
293 | &as->valid_replies[i])) | ||
294 | return ANASTASIS_AUTHORIZATION_RES_FINISHED; | ||
270 | mime = MHD_lookup_connection_value (connection, | 295 | mime = MHD_lookup_connection_value (connection, |
271 | MHD_HEADER_KIND, | 296 | MHD_HEADER_KIND, |
272 | MHD_HTTP_HEADER_ACCEPT); | 297 | MHD_HTTP_HEADER_ACCEPT); |
diff --git a/src/backend/anastasis-httpd_truth.c b/src/backend/anastasis-httpd_truth.c index 4dd3ddc..aedd0a2 100644 --- a/src/backend/anastasis-httpd_truth.c +++ b/src/backend/anastasis-httpd_truth.c | |||
@@ -882,6 +882,38 @@ return_key_share ( | |||
882 | 882 | ||
883 | 883 | ||
884 | /** | 884 | /** |
885 | * Mark @a gc as suspended and update the respective | ||
886 | * data structures and jobs. | ||
887 | * | ||
888 | * @param[in,out] gc context of the suspended operation | ||
889 | */ | ||
890 | static void | ||
891 | gc_suspended (struct GetContext *gc) | ||
892 | { | ||
893 | gc->suspended = true; | ||
894 | if (NULL == AH_to_heap) | ||
895 | AH_to_heap = GNUNET_CONTAINER_heap_create ( | ||
896 | GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
897 | gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap, | ||
898 | gc, | ||
899 | gc->timeout.abs_value_us); | ||
900 | if (NULL != to_task) | ||
901 | { | ||
902 | GNUNET_SCHEDULER_cancel (to_task); | ||
903 | to_task = NULL; | ||
904 | } | ||
905 | { | ||
906 | struct GetContext *rn; | ||
907 | |||
908 | rn = GNUNET_CONTAINER_heap_peek (AH_to_heap); | ||
909 | to_task = GNUNET_SCHEDULER_add_at (rn->timeout, | ||
910 | &do_timeout, | ||
911 | NULL); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | |||
916 | /** | ||
885 | * Run the authorization method-specific 'process' function and continue | 917 | * Run the authorization method-specific 'process' function and continue |
886 | * based on its result with generating an HTTP response. | 918 | * based on its result with generating an HTTP response. |
887 | * | 919 | * |
@@ -923,26 +955,7 @@ run_authorization_process (struct MHD_Connection *connection, | |||
923 | return MHD_YES; | 955 | return MHD_YES; |
924 | case ANASTASIS_AUTHORIZATION_RES_SUSPENDED: | 956 | case ANASTASIS_AUTHORIZATION_RES_SUSPENDED: |
925 | /* connection was suspended */ | 957 | /* connection was suspended */ |
926 | gc->suspended = true; | 958 | gc_suspended (gc); |
927 | if (NULL == AH_to_heap) | ||
928 | AH_to_heap = GNUNET_CONTAINER_heap_create ( | ||
929 | GNUNET_CONTAINER_HEAP_ORDER_MIN); | ||
930 | gc->hn = GNUNET_CONTAINER_heap_insert (AH_to_heap, | ||
931 | gc, | ||
932 | gc->timeout.abs_value_us); | ||
933 | if (NULL != to_task) | ||
934 | { | ||
935 | GNUNET_SCHEDULER_cancel (to_task); | ||
936 | to_task = NULL; | ||
937 | } | ||
938 | { | ||
939 | struct GetContext *rn; | ||
940 | |||
941 | rn = GNUNET_CONTAINER_heap_peek (AH_to_heap); | ||
942 | to_task = GNUNET_SCHEDULER_add_at (rn->timeout, | ||
943 | &do_timeout, | ||
944 | NULL); | ||
945 | } | ||
946 | return MHD_YES; | 959 | return MHD_YES; |
947 | case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED: | 960 | case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED: |
948 | /* Challenge sent successfully */ | 961 | /* Challenge sent successfully */ |
@@ -978,6 +991,212 @@ run_authorization_process (struct MHD_Connection *connection, | |||
978 | } | 991 | } |
979 | 992 | ||
980 | 993 | ||
994 | /** | ||
995 | * Use the database to rate-limit queries to the | ||
996 | * authentication procedure, but without actually | ||
997 | * storing 'real' challenge codes. | ||
998 | * | ||
999 | * @param[in,out] gc context to rate limit requests for | ||
1000 | * @return #GNUNET_OK if rate-limiting passes, | ||
1001 | * #GNUNET_NO if a reply was sent (rate limited) | ||
1002 | * #GNUNET_SYSERR if we failed and no reply | ||
1003 | * was queued | ||
1004 | */ | ||
1005 | static enum GNUNET_GenericReturnValue | ||
1006 | rate_limit (struct GetContext *gc) | ||
1007 | { | ||
1008 | enum GNUNET_DB_QueryStatus qs; | ||
1009 | struct GNUNET_TIME_Absolute rt; | ||
1010 | uint64_t code; | ||
1011 | enum ANASTASIS_DB_CodeStatus cs; | ||
1012 | struct GNUNET_HashCode hc; | ||
1013 | bool satisfied; | ||
1014 | uint64_t dummy; | ||
1015 | |||
1016 | rt = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
1017 | qs = db->create_challenge_code (db->cls, | ||
1018 | &gc->truth_uuid, | ||
1019 | MAX_QUESTION_FREQ, | ||
1020 | GNUNET_TIME_UNIT_HOURS, | ||
1021 | INITIAL_RETRY_COUNTER, | ||
1022 | &rt, | ||
1023 | &code); | ||
1024 | if (0 > qs) | ||
1025 | { | ||
1026 | GNUNET_break (0 < qs); | ||
1027 | return (MHD_YES == | ||
1028 | TALER_MHD_reply_with_error (gc->connection, | ||
1029 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
1030 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
1031 | "create_challenge_code (for rate limiting)")) | ||
1032 | ? GNUNET_NO | ||
1033 | : GNUNET_SYSERR; | ||
1034 | } | ||
1035 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
1036 | { | ||
1037 | return (MHD_YES == | ||
1038 | TALER_MHD_reply_with_error (gc->connection, | ||
1039 | MHD_HTTP_TOO_MANY_REQUESTS, | ||
1040 | TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED, | ||
1041 | NULL)) | ||
1042 | ? GNUNET_NO | ||
1043 | : GNUNET_SYSERR; | ||
1044 | } | ||
1045 | /* decrement trial counter */ | ||
1046 | ANASTASIS_hash_answer (code + 1, /* always use wrong answer */ | ||
1047 | &hc); | ||
1048 | cs = db->verify_challenge_code (db->cls, | ||
1049 | &gc->truth_uuid, | ||
1050 | &hc, | ||
1051 | &dummy, | ||
1052 | &satisfied); | ||
1053 | switch (cs) | ||
1054 | { | ||
1055 | case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: | ||
1056 | /* good, what we wanted */ | ||
1057 | return GNUNET_OK; | ||
1058 | case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: | ||
1059 | case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: | ||
1060 | GNUNET_break (0); | ||
1061 | return (MHD_YES == | ||
1062 | TALER_MHD_reply_with_error (gc->connection, | ||
1063 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
1064 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
1065 | "verify_challenge_code")) | ||
1066 | ? GNUNET_NO | ||
1067 | : GNUNET_SYSERR; | ||
1068 | case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: | ||
1069 | return (MHD_YES == | ||
1070 | TALER_MHD_reply_with_error (gc->connection, | ||
1071 | MHD_HTTP_TOO_MANY_REQUESTS, | ||
1072 | TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED, | ||
1073 | NULL)) | ||
1074 | ? GNUNET_NO | ||
1075 | : GNUNET_SYSERR; | ||
1076 | case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: | ||
1077 | /* this should be impossible, we used code+1 */ | ||
1078 | GNUNET_assert (0); | ||
1079 | } | ||
1080 | return GNUNET_SYSERR; | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | /** | ||
1085 | * Handle special case of a security question where we do not | ||
1086 | * generate a code. Rate limits answers against brute forcing. | ||
1087 | * | ||
1088 | * @param[in,out] gc request to handle | ||
1089 | * @param decrypted_truth hash to check against | ||
1090 | * @param decrypted_truth_size number of bytes in @a decrypted_truth | ||
1091 | * @return MHD status code | ||
1092 | */ | ||
1093 | static MHD_RESULT | ||
1094 | handle_security_question (struct GetContext *gc, | ||
1095 | const void *decrypted_truth, | ||
1096 | size_t decrypted_truth_size) | ||
1097 | { | ||
1098 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1099 | "Handling security question challenge\n"); | ||
1100 | if (! gc->have_response) | ||
1101 | { | ||
1102 | return TALER_MHD_reply_with_error (gc->connection, | ||
1103 | MHD_HTTP_FORBIDDEN, | ||
1104 | TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED, | ||
1105 | NULL); | ||
1106 | } | ||
1107 | /* rate limit */ | ||
1108 | { | ||
1109 | enum GNUNET_GenericReturnValue ret; | ||
1110 | |||
1111 | ret = rate_limit (gc); | ||
1112 | if (GNUNET_OK != ret) | ||
1113 | return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; | ||
1114 | } | ||
1115 | /* check reply matches truth */ | ||
1116 | if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) || | ||
1117 | (0 != memcmp (&gc->challenge_response, | ||
1118 | decrypted_truth, | ||
1119 | decrypted_truth_size)) ) | ||
1120 | { | ||
1121 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1122 | "Wrong answer provided to secure question had %u bytes, wanted %u\n", | ||
1123 | (unsigned int) decrypted_truth_size, | ||
1124 | (unsigned int) sizeof (struct GNUNET_HashCode)); | ||
1125 | return TALER_MHD_reply_with_error (gc->connection, | ||
1126 | MHD_HTTP_FORBIDDEN, | ||
1127 | TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, | ||
1128 | NULL); | ||
1129 | } | ||
1130 | /* good, return the key share */ | ||
1131 | return return_key_share (&gc->truth_uuid, | ||
1132 | gc->connection); | ||
1133 | } | ||
1134 | |||
1135 | |||
1136 | /** | ||
1137 | * Handle special case of an answer being directly checked by the | ||
1138 | * plugin and not by our database. Rate limits answers against brute | ||
1139 | * forcing. | ||
1140 | * | ||
1141 | * @param[in,out] gc request to handle | ||
1142 | * @param decrypted_truth hash to check against | ||
1143 | * @param decrypted_truth_size number of bytes in @a decrypted_truth | ||
1144 | * @return MHD status code | ||
1145 | */ | ||
1146 | static MHD_RESULT | ||
1147 | direct_validation (struct GetContext *gc, | ||
1148 | const void *decrypted_truth, | ||
1149 | size_t decrypted_truth_size) | ||
1150 | { | ||
1151 | /* Non-random code, call plugin directly! */ | ||
1152 | enum ANASTASIS_AUTHORIZATION_Result aar; | ||
1153 | enum GNUNET_GenericReturnValue res; | ||
1154 | |||
1155 | res = rate_limit (gc); | ||
1156 | if (GNUNET_OK != res) | ||
1157 | return (GNUNET_NO == res) ? MHD_YES : MHD_NO; | ||
1158 | gc->as = gc->authorization->start (gc->authorization->cls, | ||
1159 | &AH_trigger_daemon, | ||
1160 | NULL, | ||
1161 | &gc->truth_uuid, | ||
1162 | 0LLU, | ||
1163 | decrypted_truth, | ||
1164 | decrypted_truth_size); | ||
1165 | if (NULL == gc->as) | ||
1166 | { | ||
1167 | GNUNET_break (0); | ||
1168 | return TALER_MHD_reply_with_error (gc->connection, | ||
1169 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
1170 | TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED, | ||
1171 | NULL); | ||
1172 | } | ||
1173 | aar = gc->authorization->process (gc->as, | ||
1174 | GNUNET_TIME_UNIT_ZERO_ABS, | ||
1175 | gc->connection); | ||
1176 | switch (aar) | ||
1177 | { | ||
1178 | case ANASTASIS_AUTHORIZATION_RES_SUCCESS: | ||
1179 | GNUNET_break (0); | ||
1180 | return MHD_YES; | ||
1181 | case ANASTASIS_AUTHORIZATION_RES_FAILED: | ||
1182 | return MHD_YES; | ||
1183 | case ANASTASIS_AUTHORIZATION_RES_SUSPENDED: | ||
1184 | gc_suspended (gc); | ||
1185 | return MHD_YES; | ||
1186 | case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED: | ||
1187 | GNUNET_break (0); | ||
1188 | return MHD_NO; | ||
1189 | case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED: | ||
1190 | return MHD_NO; | ||
1191 | case ANASTASIS_AUTHORIZATION_RES_FINISHED: | ||
1192 | return return_key_share (&gc->truth_uuid, | ||
1193 | gc->connection); | ||
1194 | } | ||
1195 | GNUNET_break (0); | ||
1196 | return MHD_NO; | ||
1197 | } | ||
1198 | |||
1199 | |||
981 | MHD_RESULT | 1200 | MHD_RESULT |
982 | AH_handler_truth_get ( | 1201 | AH_handler_truth_get ( |
983 | struct MHD_Connection *connection, | 1202 | struct MHD_Connection *connection, |
@@ -1113,7 +1332,6 @@ AH_handler_truth_get ( | |||
1113 | GNUNET_TIME_UNIT_SECONDS); | 1332 | GNUNET_TIME_UNIT_SECONDS); |
1114 | } | 1333 | } |
1115 | } | 1334 | } |
1116 | |||
1117 | } /* end of first-time initialization (if NULL == gc) */ | 1335 | } /* end of first-time initialization (if NULL == gc) */ |
1118 | else | 1336 | else |
1119 | { | 1337 | { |
@@ -1291,104 +1509,14 @@ AH_handler_truth_get ( | |||
1291 | but check that the hash matches */ | 1509 | but check that the hash matches */ |
1292 | if (is_question) | 1510 | if (is_question) |
1293 | { | 1511 | { |
1294 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 1512 | MHD_RESULT ret; |
1295 | "Handling security question challenge\n"); | ||
1296 | if (! gc->have_response) | ||
1297 | { | ||
1298 | GNUNET_free (decrypted_truth); | ||
1299 | GNUNET_free (truth_mime); | ||
1300 | return TALER_MHD_reply_with_error (connection, | ||
1301 | MHD_HTTP_FORBIDDEN, | ||
1302 | TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED, | ||
1303 | NULL); | ||
1304 | } | ||
1305 | 1513 | ||
1306 | { | 1514 | ret = handle_security_question (gc, |
1307 | enum GNUNET_DB_QueryStatus qs; | 1515 | decrypted_truth, |
1308 | struct GNUNET_TIME_Absolute rt; | 1516 | decrypted_truth_size); |
1309 | uint64_t code; | ||
1310 | enum ANASTASIS_DB_CodeStatus cs; | ||
1311 | struct GNUNET_HashCode hc; | ||
1312 | bool satisfied; | ||
1313 | uint64_t dummy; | ||
1314 | |||
1315 | rt = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
1316 | qs = db->create_challenge_code (db->cls, | ||
1317 | &gc->truth_uuid, | ||
1318 | MAX_QUESTION_FREQ, | ||
1319 | GNUNET_TIME_UNIT_HOURS, | ||
1320 | INITIAL_RETRY_COUNTER, | ||
1321 | &rt, | ||
1322 | &code); | ||
1323 | if (0 > qs) | ||
1324 | { | ||
1325 | GNUNET_break (0 < qs); | ||
1326 | GNUNET_free (decrypted_truth); | ||
1327 | GNUNET_free (truth_mime); | ||
1328 | return TALER_MHD_reply_with_error (connection, | ||
1329 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
1330 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
1331 | "create_challenge_code (for rate limiting)"); | ||
1332 | } | ||
1333 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
1334 | { | ||
1335 | GNUNET_free (decrypted_truth); | ||
1336 | GNUNET_free (truth_mime); | ||
1337 | return TALER_MHD_reply_with_error (connection, | ||
1338 | MHD_HTTP_TOO_MANY_REQUESTS, | ||
1339 | TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED, | ||
1340 | NULL); | ||
1341 | } | ||
1342 | /* decrement trial counter */ | ||
1343 | ANASTASIS_hash_answer (code + 1, /* always use wrong answer */ | ||
1344 | &hc); | ||
1345 | cs = db->verify_challenge_code (db->cls, | ||
1346 | &gc->truth_uuid, | ||
1347 | &hc, | ||
1348 | &dummy, | ||
1349 | &satisfied); | ||
1350 | switch (cs) | ||
1351 | { | ||
1352 | case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: | ||
1353 | /* good, what we wanted */ | ||
1354 | break; | ||
1355 | case ANASTASIS_DB_CODE_STATUS_HARD_ERROR: | ||
1356 | case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR: | ||
1357 | GNUNET_break (0); | ||
1358 | return TALER_MHD_reply_with_error (gc->connection, | ||
1359 | MHD_HTTP_INTERNAL_SERVER_ERROR, | ||
1360 | TALER_EC_GENERIC_DB_FETCH_FAILED, | ||
1361 | "verify_challenge_code"); | ||
1362 | case ANASTASIS_DB_CODE_STATUS_NO_RESULTS: | ||
1363 | return TALER_MHD_reply_with_error (connection, | ||
1364 | MHD_HTTP_TOO_MANY_REQUESTS, | ||
1365 | TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED, | ||
1366 | NULL); | ||
1367 | case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED: | ||
1368 | /* this should be impossible, we used code+1 */ | ||
1369 | GNUNET_assert (0); | ||
1370 | } | ||
1371 | } | ||
1372 | if ( (decrypted_truth_size != sizeof (struct GNUNET_HashCode)) || | ||
1373 | (0 != memcmp (&gc->challenge_response, | ||
1374 | decrypted_truth, | ||
1375 | decrypted_truth_size)) ) | ||
1376 | { | ||
1377 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1378 | "Wrong answer provided to secure question had %u bytes, wanted %u\n", | ||
1379 | (unsigned int) decrypted_truth_size, | ||
1380 | (unsigned int) sizeof (struct GNUNET_HashCode)); | ||
1381 | GNUNET_free (decrypted_truth); | ||
1382 | GNUNET_free (truth_mime); | ||
1383 | return TALER_MHD_reply_with_error (connection, | ||
1384 | MHD_HTTP_FORBIDDEN, | ||
1385 | TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED, | ||
1386 | NULL); | ||
1387 | } | ||
1388 | GNUNET_free (decrypted_truth); | ||
1389 | GNUNET_free (truth_mime); | 1517 | GNUNET_free (truth_mime); |
1390 | return return_key_share (&gc->truth_uuid, | 1518 | GNUNET_free (decrypted_truth); |
1391 | connection); | 1519 | return ret; |
1392 | } | 1520 | } |
1393 | 1521 | ||
1394 | /* Not security question, check for answer in DB */ | 1522 | /* Not security question, check for answer in DB */ |
@@ -1399,6 +1527,18 @@ AH_handler_truth_get ( | |||
1399 | uint64_t code; | 1527 | uint64_t code; |
1400 | 1528 | ||
1401 | GNUNET_free (truth_mime); | 1529 | GNUNET_free (truth_mime); |
1530 | if (gc->authorization->user_provided_code) | ||
1531 | { | ||
1532 | MHD_RESULT res; | ||
1533 | |||
1534 | res = direct_validation (gc, | ||
1535 | decrypted_truth, | ||
1536 | decrypted_truth_size); | ||
1537 | GNUNET_free (decrypted_truth); | ||
1538 | return res; | ||
1539 | } | ||
1540 | |||
1541 | /* random code, check against database */ | ||
1402 | cs = db->verify_challenge_code (db->cls, | 1542 | cs = db->verify_challenge_code (db->cls, |
1403 | &gc->truth_uuid, | 1543 | &gc->truth_uuid, |
1404 | &gc->challenge_response, | 1544 | &gc->challenge_response, |
diff --git a/src/include/anastasis_database_plugin.h b/src/include/anastasis_database_plugin.h index 7bf91a2..cf5a69a 100644 --- a/src/include/anastasis_database_plugin.h +++ b/src/include/anastasis_database_plugin.h | |||
@@ -633,7 +633,7 @@ struct ANASTASIS_DatabasePlugin | |||
633 | (*mark_challenge_code_satisfied)( | 633 | (*mark_challenge_code_satisfied)( |
634 | void *cls, | 634 | void *cls, |
635 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | 635 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, |
636 | const uint64_t code); | 636 | uint64_t code); |
637 | 637 | ||
638 | 638 | ||
639 | /** | 639 | /** |
diff --git a/src/reducer/anastasis_api_recovery_redux.c b/src/reducer/anastasis_api_recovery_redux.c index 897a6dd..8709cf9 100644 --- a/src/reducer/anastasis_api_recovery_redux.c +++ b/src/reducer/anastasis_api_recovery_redux.c | |||
@@ -1684,10 +1684,13 @@ select_challenge_cb (void *cls, | |||
1684 | json_object_set_new (sctx->state, | 1684 | json_object_set_new (sctx->state, |
1685 | "selected_challenge_uuid", | 1685 | "selected_challenge_uuid", |
1686 | GNUNET_JSON_from_data_auto (&cd->uuid))); | 1686 | GNUNET_JSON_from_data_auto (&cd->uuid))); |
1687 | if (0 == strcmp ("question", | 1687 | if ( (0 == strcmp ("question", |
1688 | cd->type)) | 1688 | cd->type)) || |
1689 | (0 == strcmp ("totp", | ||
1690 | cd->type)) ) | ||
1689 | { | 1691 | { |
1690 | /* security question, immediately request user to answer it */ | 1692 | /* security question or TOTP: |
1693 | immediately request user to answer it */ | ||
1691 | set_state (sctx->state, | 1694 | set_state (sctx->state, |
1692 | ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); | 1695 | ANASTASIS_RECOVERY_STATE_CHALLENGE_SOLVING); |
1693 | sctx->cb (sctx->cb_cls, | 1696 | sctx->cb (sctx->cb_cls, |