diff options
Diffstat (limited to 'src/backend/anastasis-httpd_truth.c')
-rw-r--r-- | src/backend/anastasis-httpd_truth.c | 374 |
1 files changed, 257 insertions, 117 deletions
diff --git a/src/backend/anastasis-httpd_truth.c b/src/backend/anastasis-httpd_truth.c index 4dd3ddc..aedd0a2 100644 --- a/src/backend/anastasis-httpd_truth.c +++ b/src/backend/anastasis-httpd_truth.c | |||
@@ -882,6 +882,38 @@ return_key_share ( | |||
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, |